Browse Source

Merge branch 'unit-tests' of https://github.com/Cqoicebordel/searx into Cqoicebordel-unit-tests

Conflicts:
	searx/tests/test_engines.py
Adam Tauber 10 years ago
parent
commit
7f865356f9
45 changed files with 3692 additions and 71 deletions
  1. 1
    0
      .gitignore
  2. 5
    4
      searx/engines/bing.py
  3. 4
    1
      searx/engines/bing_images.py
  4. 12
    19
      searx/engines/bing_news.py
  5. 3
    8
      searx/engines/btdigg.py
  6. 4
    3
      searx/engines/deviantart.py
  7. 1
    1
      searx/engines/digg.py
  8. 1
    1
      searx/engines/flickr_noapi.py
  9. 3
    3
      searx/engines/google_images.py
  10. 4
    2
      searx/engines/google_news.py
  11. 4
    4
      searx/engines/kickass.py
  12. 8
    4
      searx/engines/piratebay.py
  13. 1
    1
      searx/engines/searchcode_code.py
  14. 1
    1
      searx/engines/searchcode_doc.py
  15. 4
    4
      searx/engines/stackoverflow.py
  16. 1
    2
      searx/engines/vimeo.py
  17. 6
    5
      searx/engines/www500px.py
  18. 2
    2
      searx/engines/xpath.py
  19. 2
    2
      searx/engines/youtube.py
  20. 3
    3
      searx/settings.yml
  21. 90
    0
      searx/tests/engines/test_bing.py
  22. 268
    0
      searx/tests/engines/test_bing_images.py
  23. 236
    0
      searx/tests/engines/test_bing_news.py
  24. 384
    0
      searx/tests/engines/test_btdigg.py
  25. 74
    0
      searx/tests/engines/test_dailymotion.py
  26. 57
    0
      searx/tests/engines/test_deezer.py
  27. 118
    0
      searx/tests/engines/test_deviantart.py
  28. 101
    0
      searx/tests/engines/test_digg.py
  29. 142
    0
      searx/tests/engines/test_flickr.py
  30. 442
    0
      searx/tests/engines/test_flickr_noapi.py
  31. 108
    0
      searx/tests/engines/test_google_images.py
  32. 136
    0
      searx/tests/engines/test_google_news.py
  33. 398
    0
      searx/tests/engines/test_kickass.py
  34. 67
    0
      searx/tests/engines/test_mixcloud.py
  35. 137
    0
      searx/tests/engines/test_piratebay.py
  36. 75
    0
      searx/tests/engines/test_searchcode_code.py
  37. 73
    0
      searx/tests/engines/test_searchcode_doc.py
  38. 192
    0
      searx/tests/engines/test_soundcloud.py
  39. 106
    0
      searx/tests/engines/test_stackoverflow.py
  40. 84
    0
      searx/tests/engines/test_vimeo.py
  41. 83
    0
      searx/tests/engines/test_www500px.py
  42. 204
    0
      searx/tests/engines/test_youtube.py
  43. 22
    0
      searx/tests/test_engines.py
  44. 22
    0
      searx/tests/test_utils.py
  45. 3
    1
      searx/utils.py

+ 1
- 0
.gitignore View File

23
 parts/
23
 parts/
24
 searx.egg-info/
24
 searx.egg-info/
25
 var/
25
 var/
26
+node_modules/

+ 5
- 4
searx/engines/bing.py View File

14
 from urllib import urlencode
14
 from urllib import urlencode
15
 from cgi import escape
15
 from cgi import escape
16
 from lxml import html
16
 from lxml import html
17
+from searx.engines.xpath import extract_text
17
 
18
 
18
 # engine dependent config
19
 # engine dependent config
19
 categories = ['general']
20
 categories = ['general']
55
     for result in dom.xpath('//div[@class="sa_cc"]'):
56
     for result in dom.xpath('//div[@class="sa_cc"]'):
56
         link = result.xpath('.//h3/a')[0]
57
         link = result.xpath('.//h3/a')[0]
57
         url = link.attrib.get('href')
58
         url = link.attrib.get('href')
58
-        title = ' '.join(link.xpath('.//text()'))
59
-        content = escape(' '.join(result.xpath('.//p//text()')))
59
+        title = extract_text(link)
60
+        content = escape(extract_text(result.xpath('.//p')))
60
 
61
 
61
         # append result
62
         # append result
62
         results.append({'url': url,
63
         results.append({'url': url,
71
     for result in dom.xpath('//li[@class="b_algo"]'):
72
     for result in dom.xpath('//li[@class="b_algo"]'):
72
         link = result.xpath('.//h2/a')[0]
73
         link = result.xpath('.//h2/a')[0]
73
         url = link.attrib.get('href')
74
         url = link.attrib.get('href')
74
-        title = ' '.join(link.xpath('.//text()'))
75
-        content = escape(' '.join(result.xpath('.//p//text()')))
75
+        title = extract_text(link)
76
+        content = escape(extract_text(result.xpath('.//p')))
76
 
77
 
77
         # append result
78
         # append result
78
         results.append({'url': url,
79
         results.append({'url': url,

+ 4
- 1
searx/engines/bing_images.py View File

33
     offset = (params['pageno'] - 1) * 10 + 1
33
     offset = (params['pageno'] - 1) * 10 + 1
34
 
34
 
35
     # required for cookie
35
     # required for cookie
36
-    language = 'en-US'
36
+    if params['language'] == 'all':
37
+        language = 'en-US'
38
+    else:
39
+        language = params['language'].replace('_', '-')
37
 
40
 
38
     search_path = search_string.format(
41
     search_path = search_string.format(
39
         query=urlencode({'q': query}),
42
         query=urlencode({'q': query}),

+ 12
- 19
searx/engines/bing_news.py View File

15
 from datetime import datetime, timedelta
15
 from datetime import datetime, timedelta
16
 from dateutil import parser
16
 from dateutil import parser
17
 import re
17
 import re
18
+from searx.engines.xpath import extract_text
18
 
19
 
19
 # engine dependent config
20
 # engine dependent config
20
 categories = ['news']
21
 categories = ['news']
42
     params['cookies']['_FP'] = "ui=en-US"
43
     params['cookies']['_FP'] = "ui=en-US"
43
 
44
 
44
     params['url'] = base_url + search_path
45
     params['url'] = base_url + search_path
46
+
45
     return params
47
     return params
46
 
48
 
47
 
49
 
55
     for result in dom.xpath('//div[@class="sn_r"]'):
57
     for result in dom.xpath('//div[@class="sn_r"]'):
56
         link = result.xpath('.//div[@class="newstitle"]/a')[0]
58
         link = result.xpath('.//div[@class="newstitle"]/a')[0]
57
         url = link.attrib.get('href')
59
         url = link.attrib.get('href')
58
-        title = ' '.join(link.xpath('.//text()'))
59
-        contentXPath = result.xpath('.//div[@class="sn_txt"]/div'
60
-                                    '//span[@class="sn_snip"]//text()')
61
-        if contentXPath is not None:
62
-            content = escape(' '.join(contentXPath))
60
+        title = extract_text(link)
61
+        contentXPath = result.xpath('.//div[@class="sn_txt"]/div//span[@class="sn_snip"]')
62
+        content = escape(extract_text(contentXPath))
63
 
63
 
64
         # parse publishedDate
64
         # parse publishedDate
65
         publishedDateXPath = result.xpath('.//div[@class="sn_txt"]/div'
65
         publishedDateXPath = result.xpath('.//div[@class="sn_txt"]/div'
66
                                           '//span[contains(@class,"sn_ST")]'
66
                                           '//span[contains(@class,"sn_ST")]'
67
-                                          '//span[contains(@class,"sn_tm")]'
68
-                                          '//text()')
69
-        if publishedDateXPath is not None:
70
-            publishedDate = escape(' '.join(publishedDateXPath))
67
+                                          '//span[contains(@class,"sn_tm")]')
68
+
69
+        publishedDate = escape(extract_text(publishedDateXPath))
71
 
70
 
72
         if re.match("^[0-9]+ minute(s|) ago$", publishedDate):
71
         if re.match("^[0-9]+ minute(s|) ago$", publishedDate):
73
             timeNumbers = re.findall(r'\d+', publishedDate)
72
             timeNumbers = re.findall(r'\d+', publishedDate)
74
-            publishedDate = datetime.now()\
75
-                - timedelta(minutes=int(timeNumbers[0]))
73
+            publishedDate = datetime.now() - timedelta(minutes=int(timeNumbers[0]))
76
         elif re.match("^[0-9]+ hour(s|) ago$", publishedDate):
74
         elif re.match("^[0-9]+ hour(s|) ago$", publishedDate):
77
             timeNumbers = re.findall(r'\d+', publishedDate)
75
             timeNumbers = re.findall(r'\d+', publishedDate)
78
-            publishedDate = datetime.now()\
79
-                - timedelta(hours=int(timeNumbers[0]))
80
-        elif re.match("^[0-9]+ hour(s|),"
81
-                      " [0-9]+ minute(s|) ago$", publishedDate):
76
+            publishedDate = datetime.now() - timedelta(hours=int(timeNumbers[0]))
77
+        elif re.match("^[0-9]+ hour(s|), [0-9]+ minute(s|) ago$", publishedDate):
82
             timeNumbers = re.findall(r'\d+', publishedDate)
78
             timeNumbers = re.findall(r'\d+', publishedDate)
83
             publishedDate = datetime.now()\
79
             publishedDate = datetime.now()\
84
                 - timedelta(hours=int(timeNumbers[0]))\
80
                 - timedelta(hours=int(timeNumbers[0]))\
85
                 - timedelta(minutes=int(timeNumbers[1]))
81
                 - timedelta(minutes=int(timeNumbers[1]))
86
         elif re.match("^[0-9]+ day(s|) ago$", publishedDate):
82
         elif re.match("^[0-9]+ day(s|) ago$", publishedDate):
87
             timeNumbers = re.findall(r'\d+', publishedDate)
83
             timeNumbers = re.findall(r'\d+', publishedDate)
88
-            publishedDate = datetime.now()\
89
-                - timedelta(days=int(timeNumbers[0]))
84
+            publishedDate = datetime.now() - timedelta(days=int(timeNumbers[0]))
90
         else:
85
         else:
91
             try:
86
             try:
92
-                # FIXME use params['language'] to parse either mm/dd or dd/mm
93
                 publishedDate = parser.parse(publishedDate, dayfirst=False)
87
                 publishedDate = parser.parse(publishedDate, dayfirst=False)
94
             except TypeError:
88
             except TypeError:
95
-                # FIXME
96
                 publishedDate = datetime.now()
89
                 publishedDate = datetime.now()
97
 
90
 
98
         # append result
91
         # append result

+ 3
- 8
searx/engines/btdigg.py View File

23
 url = 'https://btdigg.org'
23
 url = 'https://btdigg.org'
24
 search_url = url + '/search?q={search_term}&p={pageno}'
24
 search_url = url + '/search?q={search_term}&p={pageno}'
25
 
25
 
26
-# specific xpath variables
27
-magnet_xpath = './/a[@title="Torrent magnet link"]'
28
-torrent_xpath = './/a[@title="Download torrent file"]'
29
-content_xpath = './/span[@class="font11px lightgrey block"]'
30
-
31
 
26
 
32
 # do search-request
27
 # do search-request
33
 def request(query, params):
28
 def request(query, params):
52
     # parse results
47
     # parse results
53
     for result in search_res:
48
     for result in search_res:
54
         link = result.xpath('.//td[@class="torrent_name"]//a')[0]
49
         link = result.xpath('.//td[@class="torrent_name"]//a')[0]
55
-        href = urljoin(url, link.attrib['href'])
56
-        title = escape(extract_text(link.xpath('.//text()')))
50
+        href = urljoin(url, link.attrib.get('href'))
51
+        title = escape(extract_text(link))
57
         content = escape(extract_text(result.xpath('.//pre[@class="snippet"]')[0]))
52
         content = escape(extract_text(result.xpath('.//pre[@class="snippet"]')[0]))
58
         content = "<br />".join(content.split("\n"))
53
         content = "<br />".join(content.split("\n"))
59
 
54
 
81
                 filesize = int(filesize * 1024 * 1024 * 1024)
76
                 filesize = int(filesize * 1024 * 1024 * 1024)
82
             elif filesize_multiplier == 'MB':
77
             elif filesize_multiplier == 'MB':
83
                 filesize = int(filesize * 1024 * 1024)
78
                 filesize = int(filesize * 1024 * 1024)
84
-            elif filesize_multiplier == 'kb':
79
+            elif filesize_multiplier == 'KB':
85
                 filesize = int(filesize * 1024)
80
                 filesize = int(filesize * 1024)
86
         except:
81
         except:
87
             filesize = None
82
             filesize = None

+ 4
- 3
searx/engines/deviantart.py View File

14
 from urlparse import urljoin
14
 from urlparse import urljoin
15
 from lxml import html
15
 from lxml import html
16
 import re
16
 import re
17
+from searx.engines.xpath import extract_text
17
 
18
 
18
 # engine dependent config
19
 # engine dependent config
19
 categories = ['images']
20
 categories = ['images']
50
     for result in dom.xpath('//div[contains(@class, "tt-a tt-fh")]'):
51
     for result in dom.xpath('//div[contains(@class, "tt-a tt-fh")]'):
51
         link = result.xpath('.//a[contains(@class, "thumb")]')[0]
52
         link = result.xpath('.//a[contains(@class, "thumb")]')[0]
52
         url = urljoin(base_url, link.attrib.get('href'))
53
         url = urljoin(base_url, link.attrib.get('href'))
53
-        title_links = result.xpath('.//span[@class="details"]//a[contains(@class, "t")]')  # noqa
54
-        title = ''.join(title_links[0].xpath('.//text()'))
55
-        thumbnail_src = link.xpath('.//img')[0].attrib['src']
54
+        title_links = result.xpath('.//span[@class="details"]//a[contains(@class, "t")]')
55
+        title = extract_text(title_links[0])
56
+        thumbnail_src = link.xpath('.//img')[0].attrib.get('src')
56
         img_src = regex.sub('/', thumbnail_src)
57
         img_src = regex.sub('/', thumbnail_src)
57
 
58
 
58
         # append result
59
         # append result

+ 1
- 1
searx/engines/digg.py View File

44
 
44
 
45
     search_result = loads(resp.text)
45
     search_result = loads(resp.text)
46
 
46
 
47
-    if search_result['html'] == '':
47
+    if 'html' not in search_result or search_result['html'] == '':
48
         return results
48
         return results
49
 
49
 
50
     dom = html.fromstring(search_result['html'])
50
     dom = html.fromstring(search_result['html'])

+ 1
- 1
searx/engines/flickr_noapi.py View File

21
 categories = ['images']
21
 categories = ['images']
22
 
22
 
23
 url = 'https://secure.flickr.com/'
23
 url = 'https://secure.flickr.com/'
24
-search_url = url+'search/?{query}&page={page}'
24
+search_url = url + 'search/?{query}&page={page}'
25
 photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
25
 photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
26
 regex = re.compile(r"\"search-photos-models\",\"photos\":(.*}),\"totalItems\":", re.DOTALL)
26
 regex = re.compile(r"\"search-photos-models\",\"photos\":(.*}),\"totalItems\":", re.DOTALL)
27
 image_sizes = ('o', 'k', 'h', 'b', 'c', 'z', 'n', 'm', 't', 'q', 's')
27
 image_sizes = ('o', 'k', 'h', 'b', 'c', 'z', 'n', 'm', 't', 'q', 's')

+ 3
- 3
searx/engines/google_images.py View File

18
 
18
 
19
 # search-url
19
 # search-url
20
 url = 'https://ajax.googleapis.com/'
20
 url = 'https://ajax.googleapis.com/'
21
-search_url = url + 'ajax/services/search/images?v=1.0&start={offset}&rsz=large&safe=off&filter=off&{query}'  # noqa
21
+search_url = url + 'ajax/services/search/images?v=1.0&start={offset}&rsz=large&safe=off&filter=off&{query}'
22
 
22
 
23
 
23
 
24
 # do search-request
24
 # do search-request
45
     for result in search_res['responseData']['results']:
45
     for result in search_res['responseData']['results']:
46
         href = result['originalContextUrl']
46
         href = result['originalContextUrl']
47
         title = result['title']
47
         title = result['title']
48
-        if not result['url']:
48
+        if 'url' not in result:
49
             continue
49
             continue
50
         thumbnail_src = result['tbUrl']
50
         thumbnail_src = result['tbUrl']
51
 
51
 
52
         # append result
52
         # append result
53
         results.append({'url': href,
53
         results.append({'url': href,
54
                         'title': title,
54
                         'title': title,
55
-                        'content': '',
55
+                        'content': result['content'],
56
                         'thumbnail_src': thumbnail_src,
56
                         'thumbnail_src': thumbnail_src,
57
                         'img_src': unquote(result['url']),
57
                         'img_src': unquote(result['url']),
58
                         'template': 'images.html'})
58
                         'template': 'images.html'})

+ 4
- 2
searx/engines/google_news.py View File

20
 
20
 
21
 # engine dependent config
21
 # engine dependent config
22
 url = 'https://ajax.googleapis.com/'
22
 url = 'https://ajax.googleapis.com/'
23
-search_url = url + 'ajax/services/search/news?v=2.0&start={offset}&rsz=large&safe=off&filter=off&{query}&hl={language}'  # noqa
23
+search_url = url + 'ajax/services/search/news?v=2.0&start={offset}&rsz=large&safe=off&filter=off&{query}&hl={lang}'
24
 
24
 
25
 
25
 
26
 # do search-request
26
 # do search-request
33
 
33
 
34
     params['url'] = search_url.format(offset=offset,
34
     params['url'] = search_url.format(offset=offset,
35
                                       query=urlencode({'q': query}),
35
                                       query=urlencode({'q': query}),
36
-                                      language=language)
36
+                                      lang=language)
37
 
37
 
38
     return params
38
     return params
39
 
39
 
52
     for result in search_res['responseData']['results']:
52
     for result in search_res['responseData']['results']:
53
         # parse publishedDate
53
         # parse publishedDate
54
         publishedDate = parser.parse(result['publishedDate'])
54
         publishedDate = parser.parse(result['publishedDate'])
55
+        if 'url' not in result:
56
+            continue
55
 
57
 
56
         # append result
58
         # append result
57
         results.append({'url': result['unescapedUrl'],
59
         results.append({'url': result['unescapedUrl'],

+ 4
- 4
searx/engines/kickass.py View File

13
 from urllib import quote
13
 from urllib import quote
14
 from lxml import html
14
 from lxml import html
15
 from operator import itemgetter
15
 from operator import itemgetter
16
+from searx.engines.xpath import extract_text
16
 
17
 
17
 # engine dependent config
18
 # engine dependent config
18
 categories = ['videos', 'music', 'files']
19
 categories = ['videos', 'music', 'files']
56
     for result in search_res[1:]:
57
     for result in search_res[1:]:
57
         link = result.xpath('.//a[@class="cellMainLink"]')[0]
58
         link = result.xpath('.//a[@class="cellMainLink"]')[0]
58
         href = urljoin(url, link.attrib['href'])
59
         href = urljoin(url, link.attrib['href'])
59
-        title = ' '.join(link.xpath('.//text()'))
60
-        content = escape(html.tostring(result.xpath(content_xpath)[0],
61
-                                       method="text"))
60
+        title = extract_text(link)
61
+        content = escape(extract_text(result.xpath(content_xpath)))
62
         seed = result.xpath('.//td[contains(@class, "green")]/text()')[0]
62
         seed = result.xpath('.//td[contains(@class, "green")]/text()')[0]
63
         leech = result.xpath('.//td[contains(@class, "red")]/text()')[0]
63
         leech = result.xpath('.//td[contains(@class, "red")]/text()')[0]
64
         filesize = result.xpath('.//td[contains(@class, "nobr")]/text()')[0]
64
         filesize = result.xpath('.//td[contains(@class, "nobr")]/text()')[0]
88
                 filesize = int(filesize * 1024 * 1024 * 1024)
88
                 filesize = int(filesize * 1024 * 1024 * 1024)
89
             elif filesize_multiplier == 'MB':
89
             elif filesize_multiplier == 'MB':
90
                 filesize = int(filesize * 1024 * 1024)
90
                 filesize = int(filesize * 1024 * 1024)
91
-            elif filesize_multiplier == 'kb':
91
+            elif filesize_multiplier == 'KB':
92
                 filesize = int(filesize * 1024)
92
                 filesize = int(filesize * 1024)
93
         except:
93
         except:
94
             filesize = None
94
             filesize = None

+ 8
- 4
searx/engines/piratebay.py View File

13
 from urllib import quote
13
 from urllib import quote
14
 from lxml import html
14
 from lxml import html
15
 from operator import itemgetter
15
 from operator import itemgetter
16
+from searx.engines.xpath import extract_text
16
 
17
 
17
 # engine dependent config
18
 # engine dependent config
18
 categories = ['videos', 'music', 'files']
19
 categories = ['videos', 'music', 'files']
29
 
30
 
30
 # specific xpath variables
31
 # specific xpath variables
31
 magnet_xpath = './/a[@title="Download this torrent using magnet"]'
32
 magnet_xpath = './/a[@title="Download this torrent using magnet"]'
32
-content_xpath = './/font[@class="detDesc"]//text()'
33
+torrent_xpath = './/a[@title="Download this torrent"]'
34
+content_xpath = './/font[@class="detDesc"]'
33
 
35
 
34
 
36
 
35
 # do search-request
37
 # do search-request
59
     for result in search_res[1:]:
61
     for result in search_res[1:]:
60
         link = result.xpath('.//div[@class="detName"]//a')[0]
62
         link = result.xpath('.//div[@class="detName"]//a')[0]
61
         href = urljoin(url, link.attrib.get('href'))
63
         href = urljoin(url, link.attrib.get('href'))
62
-        title = ' '.join(link.xpath('.//text()'))
63
-        content = escape(' '.join(result.xpath(content_xpath)))
64
+        title = extract_text(link)
65
+        content = escape(extract_text(result.xpath(content_xpath)))
64
         seed, leech = result.xpath('.//td[@align="right"]/text()')[:2]
66
         seed, leech = result.xpath('.//td[@align="right"]/text()')[:2]
65
 
67
 
66
         # convert seed to int if possible
68
         # convert seed to int if possible
76
             leech = 0
78
             leech = 0
77
 
79
 
78
         magnetlink = result.xpath(magnet_xpath)[0]
80
         magnetlink = result.xpath(magnet_xpath)[0]
81
+        torrentfile = result.xpath(torrent_xpath)[0]
79
 
82
 
80
         # append result
83
         # append result
81
         results.append({'url': href,
84
         results.append({'url': href,
83
                         'content': content,
86
                         'content': content,
84
                         'seed': seed,
87
                         'seed': seed,
85
                         'leech': leech,
88
                         'leech': leech,
86
-                        'magnetlink': magnetlink.attrib['href'],
89
+                        'magnetlink': magnetlink.attrib.get('href'),
90
+                        'torrentfile': torrentfile.attrib.get('href'),
87
                         'template': 'torrent.html'})
91
                         'template': 'torrent.html'})
88
 
92
 
89
     # return results sorted by seeder
93
     # return results sorted by seeder

+ 1
- 1
searx/engines/searchcode_code.py View File

42
     search_results = loads(resp.text)
42
     search_results = loads(resp.text)
43
 
43
 
44
     # parse results
44
     # parse results
45
-    for result in search_results['results']:
45
+    for result in search_results.get('results', []):
46
         href = result['url']
46
         href = result['url']
47
         title = "" + result['name'] + " - " + result['filename']
47
         title = "" + result['name'] + " - " + result['filename']
48
         repo = result['repo']
48
         repo = result['repo']

+ 1
- 1
searx/engines/searchcode_doc.py View File

35
     search_results = loads(resp.text)
35
     search_results = loads(resp.text)
36
 
36
 
37
     # parse results
37
     # parse results
38
-    for result in search_results['results']:
38
+    for result in search_results.get('results', []):
39
         href = result['url']
39
         href = result['url']
40
         title = "[" + result['type'] + "] " +\
40
         title = "[" + result['type'] + "] " +\
41
                 result['namespace'] +\
41
                 result['namespace'] +\

+ 4
- 4
searx/engines/stackoverflow.py View File

12
 from cgi import escape
12
 from cgi import escape
13
 from urllib import urlencode
13
 from urllib import urlencode
14
 from lxml import html
14
 from lxml import html
15
+from searx.engines.xpath import extract_text
15
 
16
 
16
 # engine dependent config
17
 # engine dependent config
17
 categories = ['it']
18
 categories = ['it']
24
 # specific xpath variables
25
 # specific xpath variables
25
 results_xpath = '//div[contains(@class,"question-summary")]'
26
 results_xpath = '//div[contains(@class,"question-summary")]'
26
 link_xpath = './/div[@class="result-link"]//a|.//div[@class="summary"]//h3//a'
27
 link_xpath = './/div[@class="result-link"]//a|.//div[@class="summary"]//h3//a'
27
-title_xpath = './/text()'
28
-content_xpath = './/div[@class="excerpt"]//text()'
28
+content_xpath = './/div[@class="excerpt"]'
29
 
29
 
30
 
30
 
31
 # do search-request
31
 # do search-request
46
     for result in dom.xpath(results_xpath):
46
     for result in dom.xpath(results_xpath):
47
         link = result.xpath(link_xpath)[0]
47
         link = result.xpath(link_xpath)[0]
48
         href = urljoin(url, link.attrib.get('href'))
48
         href = urljoin(url, link.attrib.get('href'))
49
-        title = escape(' '.join(link.xpath(title_xpath)))
50
-        content = escape(' '.join(result.xpath(content_xpath)))
49
+        title = escape(extract_text(link))
50
+        content = escape(extract_text(result.xpath(content_xpath)))
51
 
51
 
52
         # append result
52
         # append result
53
         results.append({'url': href,
53
         results.append({'url': href,

+ 1
- 2
searx/engines/vimeo.py View File

59
         url = base_url + videoid
59
         url = base_url + videoid
60
         title = p.unescape(extract_text(result.xpath(title_xpath)))
60
         title = p.unescape(extract_text(result.xpath(title_xpath)))
61
         thumbnail = extract_text(result.xpath(content_xpath)[0])
61
         thumbnail = extract_text(result.xpath(content_xpath)[0])
62
-        publishedDate = parser.parse(extract_text(
63
-            result.xpath(publishedDate_xpath)[0]))
62
+        publishedDate = parser.parse(extract_text(result.xpath(publishedDate_xpath)[0]))
64
         embedded = embedded_url.format(videoid=videoid)
63
         embedded = embedded_url.format(videoid=videoid)
65
 
64
 
66
         # append result
65
         # append result

+ 6
- 5
searx/engines/www500px.py View File

15
 from urlparse import urljoin
15
 from urlparse import urljoin
16
 from lxml import html
16
 from lxml import html
17
 import re
17
 import re
18
+from searx.engines.xpath import extract_text
18
 
19
 
19
 # engine dependent config
20
 # engine dependent config
20
 categories = ['images']
21
 categories = ['images']
22
 
23
 
23
 # search-url
24
 # search-url
24
 base_url = 'https://500px.com'
25
 base_url = 'https://500px.com'
25
-search_url = base_url+'/search?search?page={pageno}&type=photos&{query}'
26
+search_url = base_url + '/search?search?page={pageno}&type=photos&{query}'
26
 
27
 
27
 
28
 
28
 # do search-request
29
 # do search-request
44
     for result in dom.xpath('//div[@class="photo"]'):
45
     for result in dom.xpath('//div[@class="photo"]'):
45
         link = result.xpath('.//a')[0]
46
         link = result.xpath('.//a')[0]
46
         url = urljoin(base_url, link.attrib.get('href'))
47
         url = urljoin(base_url, link.attrib.get('href'))
47
-        title = result.xpath('.//div[@class="title"]//text()')[0]
48
-        thumbnail_src = link.xpath('.//img')[0].attrib['src']
48
+        title = extract_text(result.xpath('.//div[@class="title"]'))
49
+        thumbnail_src = link.xpath('.//img')[0].attrib.get('src')
49
         # To have a bigger thumbnail, uncomment the next line
50
         # To have a bigger thumbnail, uncomment the next line
50
-        #thumbnail_src = regex.sub('4.jpg', thumbnail_src)
51
-        content = result.xpath('.//div[@class="info"]//text()')[0]
51
+        # thumbnail_src = regex.sub('4.jpg', thumbnail_src)
52
+        content = extract_text(result.xpath('.//div[@class="info"]'))
52
         img_src = regex.sub('2048.jpg', thumbnail_src)
53
         img_src = regex.sub('2048.jpg', thumbnail_src)
53
 
54
 
54
         # append result
55
         # append result

+ 2
- 2
searx/engines/xpath.py View File

28
         result = ''
28
         result = ''
29
         for e in xpath_results:
29
         for e in xpath_results:
30
             result = result + extract_text(e)
30
             result = result + extract_text(e)
31
-        return result
31
+        return result.strip()
32
     elif type(xpath_results) in [_ElementStringResult, _ElementUnicodeResult]:
32
     elif type(xpath_results) in [_ElementStringResult, _ElementUnicodeResult]:
33
         # it's a string
33
         # it's a string
34
         return ''.join(xpath_results)
34
         return ''.join(xpath_results)
35
     else:
35
     else:
36
         # it's a element
36
         # it's a element
37
-        return html_to_text(xpath_results.text_content())
37
+        return html_to_text(xpath_results.text_content()).strip()
38
 
38
 
39
 
39
 
40
 def extract_url(xpath_results, search_url):
40
 def extract_url(xpath_results, search_url):

+ 2
- 2
searx/engines/youtube.py View File

57
         url = [x['href'] for x in result['link'] if x['type'] == 'text/html']
57
         url = [x['href'] for x in result['link'] if x['type'] == 'text/html']
58
 
58
 
59
         if not url:
59
         if not url:
60
-            return
60
+            continue
61
 
61
 
62
         # remove tracking
62
         # remove tracking
63
         url = url[0].replace('feature=youtube_gdata', '')
63
         url = url[0].replace('feature=youtube_gdata', '')
73
         pubdate = result['published']['$t']
73
         pubdate = result['published']['$t']
74
         publishedDate = parser.parse(pubdate)
74
         publishedDate = parser.parse(pubdate)
75
 
75
 
76
-        if result['media$group']['media$thumbnail']:
76
+        if 'media$thumbnail' in result['media$group']:
77
             thumbnail = result['media$group']['media$thumbnail'][0]['url']
77
             thumbnail = result['media$group']['media$thumbnail'][0]['url']
78
 
78
 
79
         content = result['content']['$t']
79
         content = result['content']['$t']

+ 3
- 3
searx/settings.yml View File

161
     engine : photon
161
     engine : photon
162
     shortcut : ph
162
     shortcut : ph
163
 
163
 
164
-#  - name : piratebay
165
-#    engine : piratebay
166
-#    shortcut : tpb
164
+  - name : piratebay
165
+    engine : piratebay
166
+    shortcut : tpb
167
 
167
 
168
   - name : kickass
168
   - name : kickass
169
     engine : kickass
169
     engine : kickass

+ 90
- 0
searx/tests/engines/test_bing.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import bing
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestBingEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        dicto['language'] = 'fr_FR'
14
+        params = bing.request(query, dicto)
15
+        self.assertTrue('url' in params)
16
+        self.assertTrue(query in params['url'])
17
+        self.assertTrue('bing.com' in params['url'])
18
+        self.assertTrue('SRCHHPGUSR' in params['cookies'])
19
+        self.assertTrue('fr' in params['cookies']['SRCHHPGUSR'])
20
+
21
+        dicto['language'] = 'all'
22
+        params = bing.request(query, dicto)
23
+        self.assertTrue('SRCHHPGUSR' in params['cookies'])
24
+        self.assertTrue('en' in params['cookies']['SRCHHPGUSR'])
25
+
26
+    def test_response(self):
27
+        self.assertRaises(AttributeError, bing.response, None)
28
+        self.assertRaises(AttributeError, bing.response, [])
29
+        self.assertRaises(AttributeError, bing.response, '')
30
+        self.assertRaises(AttributeError, bing.response, '[]')
31
+
32
+        response = mock.Mock(content='<html></html>')
33
+        self.assertEqual(bing.response(response), [])
34
+
35
+        response = mock.Mock(content='<html></html>')
36
+        self.assertEqual(bing.response(response), [])
37
+
38
+        html = """
39
+        <div class="sa_cc" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
40
+            <div Class="sa_mc">
41
+                <div class="sb_tlst">
42
+                    <h3>
43
+                        <a href="http://this.should.be.the.link/" h="ID=SERP,5124.1">
44
+                        <strong>This</strong> should be the title</a>
45
+                    </h3>
46
+                </div>
47
+                <div class="sb_meta"><cite><strong>this</strong>.meta.com</cite>
48
+                    <span class="c_tlbxTrg">
49
+                        <span class="c_tlbxH" H="BASE:CACHEDPAGEDEFAULT" K="SERP,5125.1">
50
+                        </span>
51
+                    </span>
52
+                </div>
53
+                <p><strong>This</strong> should be the content.</p>
54
+            </div>
55
+        </div>
56
+        """
57
+        response = mock.Mock(content=html)
58
+        results = bing.response(response)
59
+        self.assertEqual(type(results), list)
60
+        self.assertEqual(len(results), 1)
61
+        self.assertEqual(results[0]['title'], 'This should be the title')
62
+        self.assertEqual(results[0]['url'], 'http://this.should.be.the.link/')
63
+        self.assertEqual(results[0]['content'], 'This should be the content.')
64
+
65
+        html = """
66
+        <li class="b_algo" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
67
+            <div Class="sa_mc">
68
+                <div class="sb_tlst">
69
+                    <h2>
70
+                        <a href="http://this.should.be.the.link/" h="ID=SERP,5124.1">
71
+                        <strong>This</strong> should be the title</a>
72
+                    </h2>
73
+                </div>
74
+                <div class="sb_meta"><cite><strong>this</strong>.meta.com</cite>
75
+                    <span class="c_tlbxTrg">
76
+                        <span class="c_tlbxH" H="BASE:CACHEDPAGEDEFAULT" K="SERP,5125.1">
77
+                        </span>
78
+                    </span>
79
+                </div>
80
+                <p><strong>This</strong> should be the content.</p>
81
+            </div>
82
+        </li>
83
+        """
84
+        response = mock.Mock(content=html)
85
+        results = bing.response(response)
86
+        self.assertEqual(type(results), list)
87
+        self.assertEqual(len(results), 1)
88
+        self.assertEqual(results[0]['title'], 'This should be the title')
89
+        self.assertEqual(results[0]['url'], 'http://this.should.be.the.link/')
90
+        self.assertEqual(results[0]['content'], 'This should be the content.')

+ 268
- 0
searx/tests/engines/test_bing_images.py View File

1
+# -*- coding: utf-8 -*-
2
+from collections import defaultdict
3
+import mock
4
+from searx.engines import bing_images
5
+from searx.testing import SearxTestCase
6
+
7
+
8
+class TestBingImagesEngine(SearxTestCase):
9
+
10
+    def test_request(self):
11
+        query = 'test_query'
12
+        dicto = defaultdict(dict)
13
+        dicto['pageno'] = 1
14
+        dicto['language'] = 'fr_FR'
15
+        params = bing_images.request(query, dicto)
16
+        self.assertTrue('url' in params)
17
+        self.assertTrue(query in params['url'])
18
+        self.assertTrue('bing.com' in params['url'])
19
+        self.assertTrue('SRCHHPGUSR' in params['cookies'])
20
+        self.assertTrue('fr' in params['cookies']['SRCHHPGUSR'])
21
+
22
+        dicto['language'] = 'all'
23
+        params = bing_images.request(query, dicto)
24
+        self.assertIn('SRCHHPGUSR', params['cookies'])
25
+        self.assertIn('en', params['cookies']['SRCHHPGUSR'])
26
+
27
+    def test_response(self):
28
+        self.assertRaises(AttributeError, bing_images.response, None)
29
+        self.assertRaises(AttributeError, bing_images.response, [])
30
+        self.assertRaises(AttributeError, bing_images.response, '')
31
+        self.assertRaises(AttributeError, bing_images.response, '[]')
32
+
33
+        response = mock.Mock(content='<html></html>')
34
+        self.assertEqual(bing_images.response(response), [])
35
+
36
+        response = mock.Mock(content='<html></html>')
37
+        self.assertEqual(bing_images.response(response), [])
38
+
39
+        html = """
40
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
41
+            <a href="#" ihk="HN.608003696942779811"
42
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
43
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
44
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
45
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
46
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
47
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
48
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
49
+                style="height:144px;" width="178" height="144"/>
50
+            </a>
51
+        </div>
52
+        """
53
+        html = html.replace('\r\n', '').replace('\n', '').replace('\r', '')
54
+        response = mock.Mock(content=html)
55
+        results = bing_images.response(response)
56
+        self.assertEqual(type(results), list)
57
+        self.assertEqual(len(results), 1)
58
+        self.assertEqual(results[0]['title'], 'Test Query')
59
+        self.assertEqual(results[0]['url'], 'http://www.page.url/')
60
+        self.assertEqual(results[0]['content'], '')
61
+        self.assertEqual(results[0]['thumbnail_src'], 'http://ts1.mm.bing.net/th?id=HN.608003696942779811')
62
+        self.assertEqual(results[0]['img_src'], 'http://test.url/Test%20Query.jpg')
63
+
64
+        html = """
65
+        <a href="#" ihk="HN.608003696942779811"
66
+            m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
67
+            mid:&quot;59EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
68
+            surl:&quot;http://www.page.url/&quot;,
69
+            imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,oh:&quot;238&quot;,
70
+            tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
71
+            mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
72
+            t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
73
+            <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
74
+            style="height:144px;" width="178" height="144"/>
75
+        </a>
76
+        """
77
+        response = mock.Mock(content=html)
78
+        results = bing_images.response(response)
79
+        self.assertEqual(type(results), list)
80
+        self.assertEqual(len(results), 0)
81
+
82
+        html = """
83
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
84
+            <a href="#" ihk="HN.608003696942779811"
85
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
86
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
87
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
88
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
89
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
90
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
91
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
92
+                style="height:144px;" width="178" height="144"/>
93
+            </a>
94
+        </div>
95
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
96
+            <a href="#" ihk="HN.608003696942779811"
97
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
98
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
99
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
100
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
101
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
102
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
103
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
104
+                style="height:144px;" width="178" height="144"/>
105
+            </a>
106
+        </div>
107
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
108
+            <a href="#" ihk="HN.608003696942779811"
109
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
110
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
111
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
112
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
113
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
114
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
115
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
116
+                style="height:144px;" width="178" height="144"/>
117
+            </a>
118
+        </div>
119
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
120
+            <a href="#" ihk="HN.608003696942779811"
121
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
122
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
123
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
124
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
125
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
126
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
127
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
128
+                style="height:144px;" width="178" height="144"/>
129
+            </a>
130
+        </div>
131
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
132
+            <a href="#" ihk="HN.608003696942779811"
133
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
134
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
135
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
136
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
137
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
138
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
139
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
140
+                style="height:144px;" width="178" height="144"/>
141
+            </a>
142
+        </div>
143
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
144
+            <a href="#" ihk="HN.608003696942779811"
145
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
146
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
147
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
148
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
149
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
150
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
151
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
152
+                style="height:144px;" width="178" height="144"/>
153
+            </a>
154
+        </div>
155
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
156
+            <a href="#" ihk="HN.608003696942779811"
157
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
158
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
159
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
160
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
161
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
162
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
163
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
164
+                style="height:144px;" width="178" height="144"/>
165
+            </a>
166
+        </div>
167
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
168
+            <a href="#" ihk="HN.608003696942779811"
169
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
170
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
171
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
172
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
173
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
174
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
175
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
176
+                style="height:144px;" width="178" height="144"/>
177
+            </a>
178
+        </div>
179
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
180
+            <a href="#" ihk="HN.608003696942779811"
181
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
182
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
183
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
184
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
185
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
186
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
187
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
188
+                style="height:144px;" width="178" height="144"/>
189
+            </a>
190
+        </div>
191
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
192
+            <a href="#" ihk="HN.608003696942779811"
193
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
194
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
195
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
196
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
197
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
198
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
199
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
200
+                style="height:144px;" width="178" height="144"/>
201
+            </a>
202
+        </div>
203
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
204
+            <a href="#" ihk="HN.608003696942779811"
205
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
206
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
207
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
208
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
209
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
210
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
211
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
212
+                style="height:144px;" width="178" height="144"/>
213
+            </a>
214
+        </div>
215
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
216
+            <a href="#" ihk="HN.608003696942779811"
217
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
218
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
219
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
220
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
221
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
222
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
223
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
224
+                style="height:144px;" width="178" height="144"/>
225
+            </a>
226
+        </div>
227
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
228
+            <a href="#" ihk="HN.608003696942779811"
229
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
230
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
231
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
232
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
233
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
234
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
235
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
236
+                style="height:144px;" width="178" height="144"/>
237
+            </a>
238
+        </div>
239
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
240
+            <a href="#" ihk="HN.608003696942779811"
241
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
242
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
243
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
244
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
245
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
246
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
247
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
248
+                style="height:144px;" width="178" height="144"/>
249
+            </a>
250
+        </div>
251
+        <div class="dg_u" style="width:178px;height:144px;left:17px;top:0px">
252
+            <a href="#" ihk="HN.608003696942779811"
253
+                m="{ns:&quot;images&quot;,k:&quot;5045&quot;,
254
+mid:&quot;659EB92C317974F34517A1CCAEBEF76A578E08DEE&quot;,
255
+surl:&quot;http://www.page.url/&quot;,imgurl:&quot;http://test.url/Test%20Query.jpg&quot;,
256
+oh:&quot;238&quot;,tft:&quot;0&quot;,oi:&quot;http://www.image.url/Images/Test%20Query.jpg&quot;}"
257
+                mid="59EB92C317974F34517A1CCAEBEF76A578E08DEE" onclick="return false;"
258
+                t1="Test Query" t2="650 x 517 · 31 kB · jpeg" t3="www.short.url" h="ID=images,5045.1">
259
+                <img src="https://tse4.mm.bing.net/th?id=HN.608003696942779811&amp;o=4&amp;pid=1.7"
260
+                style="height:144px;" width="178" height="144"/>
261
+            </a>
262
+        </div>
263
+        """
264
+        html = html.replace('\r\n', '').replace('\n', '').replace('\r', '')
265
+        response = mock.Mock(content=html)
266
+        results = bing_images.response(response)
267
+        self.assertEqual(type(results), list)
268
+        self.assertEqual(len(results), 10)

+ 236
- 0
searx/tests/engines/test_bing_news.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import bing_news
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestBingNewsEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 1
13
+        dicto['language'] = 'fr_FR'
14
+        params = bing_news.request(query, dicto)
15
+        self.assertIn('url', params)
16
+        self.assertIn(query, params['url'])
17
+        self.assertIn('bing.com', params['url'])
18
+        self.assertIn('fr', params['url'])
19
+        self.assertIn('_FP', params['cookies'])
20
+        self.assertIn('en', params['cookies']['_FP'])
21
+
22
+        dicto['language'] = 'all'
23
+        params = bing_news.request(query, dicto)
24
+        self.assertIn('en', params['url'])
25
+        self.assertIn('_FP', params['cookies'])
26
+        self.assertIn('en', params['cookies']['_FP'])
27
+
28
+    def test_response(self):
29
+        self.assertRaises(AttributeError, bing_news.response, None)
30
+        self.assertRaises(AttributeError, bing_news.response, [])
31
+        self.assertRaises(AttributeError, bing_news.response, '')
32
+        self.assertRaises(AttributeError, bing_news.response, '[]')
33
+
34
+        response = mock.Mock(content='<html></html>')
35
+        self.assertEqual(bing_news.response(response), [])
36
+
37
+        response = mock.Mock(content='<html></html>')
38
+        self.assertEqual(bing_news.response(response), [])
39
+
40
+        html = """
41
+        <div class="sn_r">
42
+            <div class="newstitle">
43
+                <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
44
+                    Title
45
+                </a>
46
+            </div>
47
+            <div class="sn_img">
48
+                <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
49
+                    <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
50
+                </a>
51
+            </div>
52
+            <div class="sn_txt">
53
+                <div class="sn_oi">
54
+                    <span class="sn_snip">Article Content</span>
55
+                    <span class="sn_ST">
56
+                        <cite class="sn_src">metronews.fr</cite>
57
+                        &nbsp;&#0183;&#32;
58
+                        <span class="sn_tm">44 minutes ago</span>
59
+                    </span>
60
+                </div>
61
+            </div>
62
+        </div>
63
+        """
64
+        response = mock.Mock(content=html)
65
+        results = bing_news.response(response)
66
+        self.assertEqual(type(results), list)
67
+        self.assertEqual(len(results), 1)
68
+        self.assertEqual(results[0]['title'], 'Title')
69
+        self.assertEqual(results[0]['url'], 'http://url.of.article/')
70
+        self.assertEqual(results[0]['content'], 'Article Content')
71
+
72
+        html = """
73
+        <div class="sn_r">
74
+            <div class="newstitle">
75
+                <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
76
+                    Title
77
+                </a>
78
+            </div>
79
+            <div class="sn_img">
80
+                <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
81
+                    <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
82
+                </a>
83
+            </div>
84
+            <div class="sn_txt">
85
+                <div class="sn_oi">
86
+                    <span class="sn_snip">Article Content</span>
87
+                    <span class="sn_ST">
88
+                        <cite class="sn_src">metronews.fr</cite>
89
+                        &nbsp;&#0183;&#32;
90
+                        <span class="sn_tm">44 minutes ago</span>
91
+                    </span>
92
+                </div>
93
+            </div>
94
+        </div>
95
+        <div class="sn_r">
96
+            <div class="newstitle">
97
+                <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
98
+                    Title
99
+                </a>
100
+            </div>
101
+            <div class="sn_img">
102
+                <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
103
+                    <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
104
+                </a>
105
+            </div>
106
+            <div class="sn_txt">
107
+                <div class="sn_oi">
108
+                    <span class="sn_snip">Article Content</span>
109
+                    <span class="sn_ST">
110
+                        <cite class="sn_src">metronews.fr</cite>
111
+                        &nbsp;&#0183;&#32;
112
+                        <span class="sn_tm">3 hours, 44 minutes ago</span>
113
+                    </span>
114
+                </div>
115
+            </div>
116
+        </div>
117
+        <div class="sn_r">
118
+            <div class="newstitle">
119
+                <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
120
+                    Title
121
+                </a>
122
+            </div>
123
+            <div class="sn_img">
124
+                <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
125
+                    <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
126
+                </a>
127
+            </div>
128
+            <div class="sn_txt">
129
+                <div class="sn_oi">
130
+                    <span class="sn_snip">Article Content</span>
131
+                    <span class="sn_ST">
132
+                        <cite class="sn_src">metronews.fr</cite>
133
+                        &nbsp;&#0183;&#32;
134
+                        <span class="sn_tm">44 hours ago</span>
135
+                    </span>
136
+                </div>
137
+            </div>
138
+        </div>
139
+        <div class="sn_r">
140
+            <div class="newstitle">
141
+                <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
142
+                    Title
143
+                </a>
144
+            </div>
145
+            <div class="sn_img">
146
+                <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
147
+                    <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
148
+                </a>
149
+            </div>
150
+            <div class="sn_txt">
151
+                <div class="sn_oi">
152
+                    <span class="sn_snip">Article Content</span>
153
+                    <span class="sn_ST">
154
+                        <cite class="sn_src">metronews.fr</cite>
155
+                        &nbsp;&#0183;&#32;
156
+                        <span class="sn_tm">2 days ago</span>
157
+                    </span>
158
+                </div>
159
+            </div>
160
+        </div>
161
+        <div class="sn_r">
162
+            <div class="newstitle">
163
+                <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
164
+                    Title
165
+                </a>
166
+            </div>
167
+            <div class="sn_img">
168
+                <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
169
+                    <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
170
+                </a>
171
+            </div>
172
+            <div class="sn_txt">
173
+                <div class="sn_oi">
174
+                    <span class="sn_snip">Article Content</span>
175
+                    <span class="sn_ST">
176
+                        <cite class="sn_src">metronews.fr</cite>
177
+                        &nbsp;&#0183;&#32;
178
+                        <span class="sn_tm">27/01/2015</span>
179
+                    </span>
180
+                </div>
181
+            </div>
182
+        </div>
183
+        <div class="sn_r">
184
+            <div class="newstitle">
185
+                <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
186
+                    Title
187
+                </a>
188
+            </div>
189
+            <div class="sn_img">
190
+                <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
191
+                    <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
192
+                </a>
193
+            </div>
194
+            <div class="sn_txt">
195
+                <div class="sn_oi">
196
+                    <span class="sn_snip">Article Content</span>
197
+                    <span class="sn_ST">
198
+                        <cite class="sn_src">metronews.fr</cite>
199
+                        &nbsp;&#0183;&#32;
200
+                        <span class="sn_tm">Il y a 3 heures</span>
201
+                    </span>
202
+                </div>
203
+            </div>
204
+        </div>
205
+        """
206
+        response = mock.Mock(content=html)
207
+        results = bing_news.response(response)
208
+        self.assertEqual(type(results), list)
209
+        self.assertEqual(len(results), 6)
210
+
211
+        html = """
212
+        <div class="newstitle">
213
+            <a href="http://url.of.article/" target="_blank" h="ID=news,5022.1">
214
+                Title
215
+            </a>
216
+        </div>
217
+        <div class="sn_img">
218
+            <a href="http://url.of.article2/" target="_blank" h="ID=news,5024.1">
219
+                <img class="rms_img" height="80" id="emb1" src="/image.src" title="Title" width="80" />
220
+            </a>
221
+        </div>
222
+        <div class="sn_txt">
223
+            <div class="sn_oi">
224
+                <span class="sn_snip">Article Content</span>
225
+                <span class="sn_ST">
226
+                    <cite class="sn_src">metronews.fr</cite>
227
+                    &nbsp;&#0183;&#32;
228
+                    <span class="sn_tm">44 minutes ago</span>
229
+                </span>
230
+            </div>
231
+        </div>
232
+        """
233
+        response = mock.Mock(content=html)
234
+        results = bing_news.response(response)
235
+        self.assertEqual(type(results), list)
236
+        self.assertEqual(len(results), 0)

+ 384
- 0
searx/tests/engines/test_btdigg.py View File

1
+# -*- coding: utf-8 -*-
2
+from collections import defaultdict
3
+import mock
4
+from searx.engines import btdigg
5
+from searx.testing import SearxTestCase
6
+
7
+
8
+class TestBtdiggEngine(SearxTestCase):
9
+
10
+    def test_request(self):
11
+        query = 'test_query'
12
+        dicto = defaultdict(dict)
13
+        dicto['pageno'] = 0
14
+        params = btdigg.request(query, dicto)
15
+        self.assertIn('url', params)
16
+        self.assertIn(query, params['url'])
17
+        self.assertIn('btdigg.org', params['url'])
18
+
19
+    def test_response(self):
20
+        self.assertRaises(AttributeError, btdigg.response, None)
21
+        self.assertRaises(AttributeError, btdigg.response, [])
22
+        self.assertRaises(AttributeError, btdigg.response, '')
23
+        self.assertRaises(AttributeError, btdigg.response, '[]')
24
+
25
+        response = mock.Mock(text='<html></html>')
26
+        self.assertEqual(btdigg.response(response), [])
27
+
28
+        html = """
29
+        <div id="search_res">
30
+            <table>
31
+                <tr>
32
+                    <td class="idx">1</td>
33
+                    <td>
34
+                        <table class="torrent_name_tbl">
35
+                            <tr>
36
+                                <td class="torrent_name">
37
+                                    <a href="/url">Should be the title</a>
38
+                                </td>
39
+                            </tr>
40
+                        </table>
41
+                        <table class="torrent_name_tbl">
42
+                            <tr>
43
+                                <td class="ttth">
44
+                                    <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
45
+                                    title="Télécharger des liens Magnet">[magnet]</a>
46
+                                </td>
47
+                                <td class="ttth">
48
+                                    <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
49
+                                    target="_blank" title="Ajouter à BTCloud">[cloud]</a>
50
+                                </td>
51
+                                <td>
52
+                                    <span class="attr_name">Taille:</span>
53
+                                    <span class="attr_val">8 B</span>
54
+                                </td>
55
+                                <td>
56
+                                    <span class="attr_name">Fichiers:</span>
57
+                                    <span class="attr_val">710</span>
58
+                                </td>
59
+                                <td>
60
+                                    <span class="attr_name">Téléchargements:</span>
61
+                                    <span class="attr_val">5</span>
62
+                                </td>
63
+                                <td>
64
+                                    <span class="attr_name">Temps:</span>
65
+                                    <span class="attr_val">417.8&nbsp;jours</span>
66
+                                </td>
67
+                                <td>
68
+                                    <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
69
+                                    <span class="attr_val">5.3&nbsp;jours</span>
70
+                                </td>
71
+                                <td>
72
+                                    <span class="attr_name">Faux:</span>
73
+                                    <span class="attr_val">Aucun</span>
74
+                                </td>
75
+                            </tr>
76
+                        </table>
77
+                        <pre class="snippet">
78
+                            Content
79
+                        </pre>
80
+                    </td>
81
+                </tr>
82
+            </table>
83
+        </div>
84
+        """
85
+        response = mock.Mock(text=html)
86
+        results = btdigg.response(response)
87
+        self.assertEqual(type(results), list)
88
+        self.assertEqual(len(results), 1)
89
+        self.assertEqual(results[0]['title'], 'Should be the title')
90
+        self.assertEqual(results[0]['url'], 'https://btdigg.org/url')
91
+        self.assertEqual(results[0]['content'], 'Content')
92
+        self.assertEqual(results[0]['seed'], 5)
93
+        self.assertEqual(results[0]['leech'], 0)
94
+        self.assertEqual(results[0]['filesize'], 8)
95
+        self.assertEqual(results[0]['files'], 710)
96
+        self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:magnet&dn=Test')
97
+
98
+        html = """
99
+        <div id="search_res">
100
+            <table>
101
+            </table>
102
+        </div>
103
+        """
104
+        response = mock.Mock(text=html)
105
+        results = btdigg.response(response)
106
+        self.assertEqual(type(results), list)
107
+        self.assertEqual(len(results), 0)
108
+
109
+        html = """
110
+        <div id="search_res">
111
+            <table>
112
+                <tr>
113
+                    <td class="idx">1</td>
114
+                    <td>
115
+                        <table class="torrent_name_tbl">
116
+                            <tr>
117
+                                <td class="torrent_name">
118
+                                    <a href="/url">Should be the title</a>
119
+                                </td>
120
+                            </tr>
121
+                        </table>
122
+                        <table class="torrent_name_tbl">
123
+                            <tr>
124
+                                <td class="ttth">
125
+                                    <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
126
+                                    title="Télécharger des liens Magnet">[magnet]</a>
127
+                                </td>
128
+                                <td class="ttth">
129
+                                    <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
130
+                                    target="_blank" title="Ajouter à BTCloud">[cloud]</a>
131
+                                </td>
132
+                                <td>
133
+                                    <span class="attr_name">Taille:</span>
134
+                                    <span class="attr_val">1 KB</span>
135
+                                </td>
136
+                                <td>
137
+                                    <span class="attr_name">Fichiers:</span>
138
+                                    <span class="attr_val">710</span>
139
+                                </td>
140
+                                <td>
141
+                                    <span class="attr_name">Téléchargements:</span>
142
+                                    <span class="attr_val">5</span>
143
+                                </td>
144
+                                <td>
145
+                                    <span class="attr_name">Temps:</span>
146
+                                    <span class="attr_val">417.8&nbsp;jours</span>
147
+                                </td>
148
+                                <td>
149
+                                    <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
150
+                                    <span class="attr_val">5.3&nbsp;jours</span>
151
+                                </td>
152
+                                <td>
153
+                                    <span class="attr_name">Faux:</span>
154
+                                    <span class="attr_val">Aucun</span>
155
+                                </td>
156
+                            </tr>
157
+                        </table>
158
+                        <pre class="snippet">
159
+                            Content
160
+                        </pre>
161
+                    </td>
162
+                </tr>
163
+                <tr>
164
+                    <td class="idx">1</td>
165
+                    <td>
166
+                        <table class="torrent_name_tbl">
167
+                            <tr>
168
+                                <td class="torrent_name">
169
+                                    <a href="/url">Should be the title</a>
170
+                                </td>
171
+                            </tr>
172
+                        </table>
173
+                        <table class="torrent_name_tbl">
174
+                            <tr>
175
+                                <td class="ttth">
176
+                                    <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
177
+                                    title="Télécharger des liens Magnet">[magnet]</a>
178
+                                </td>
179
+                                <td class="ttth">
180
+                                    <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
181
+                                    target="_blank" title="Ajouter à BTCloud">[cloud]</a>
182
+                                </td>
183
+                                <td>
184
+                                    <span class="attr_name">Taille:</span>
185
+                                    <span class="attr_val">1 MB</span>
186
+                                </td>
187
+                                <td>
188
+                                    <span class="attr_name">Fichiers:</span>
189
+                                    <span class="attr_val">a</span>
190
+                                </td>
191
+                                <td>
192
+                                    <span class="attr_name">Téléchargements:</span>
193
+                                    <span class="attr_val">4</span>
194
+                                </td>
195
+                                <td>
196
+                                    <span class="attr_name">Temps:</span>
197
+                                    <span class="attr_val">417.8&nbsp;jours</span>
198
+                                </td>
199
+                                <td>
200
+                                    <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
201
+                                    <span class="attr_val">5.3&nbsp;jours</span>
202
+                                </td>
203
+                                <td>
204
+                                    <span class="attr_name">Faux:</span>
205
+                                    <span class="attr_val">Aucun</span>
206
+                                </td>
207
+                            </tr>
208
+                        </table>
209
+                        <pre class="snippet">
210
+                            Content
211
+                        </pre>
212
+                    </td>
213
+                </tr>
214
+                <tr>
215
+                    <td class="idx">1</td>
216
+                    <td>
217
+                        <table class="torrent_name_tbl">
218
+                            <tr>
219
+                                <td class="torrent_name">
220
+                                    <a href="/url">Should be the title</a>
221
+                                </td>
222
+                            </tr>
223
+                        </table>
224
+                        <table class="torrent_name_tbl">
225
+                            <tr>
226
+                                <td class="ttth">
227
+                                    <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
228
+                                    title="Télécharger des liens Magnet">[magnet]</a>
229
+                                </td>
230
+                                <td class="ttth">
231
+                                    <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
232
+                                    target="_blank" title="Ajouter à BTCloud">[cloud]</a>
233
+                                </td>
234
+                                <td>
235
+                                    <span class="attr_name">Taille:</span>
236
+                                    <span class="attr_val">1 GB</span>
237
+                                </td>
238
+                                <td>
239
+                                    <span class="attr_name">Fichiers:</span>
240
+                                    <span class="attr_val">710</span>
241
+                                </td>
242
+                                <td>
243
+                                    <span class="attr_name">Téléchargements:</span>
244
+                                    <span class="attr_val">3</span>
245
+                                </td>
246
+                                <td>
247
+                                    <span class="attr_name">Temps:</span>
248
+                                    <span class="attr_val">417.8&nbsp;jours</span>
249
+                                </td>
250
+                                <td>
251
+                                    <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
252
+                                    <span class="attr_val">5.3&nbsp;jours</span>
253
+                                </td>
254
+                                <td>
255
+                                    <span class="attr_name">Faux:</span>
256
+                                    <span class="attr_val">Aucun</span>
257
+                                </td>
258
+                            </tr>
259
+                        </table>
260
+                        <pre class="snippet">
261
+                            Content
262
+                        </pre>
263
+                    </td>
264
+                </tr>
265
+                <tr>
266
+                    <td class="idx">1</td>
267
+                    <td>
268
+                        <table class="torrent_name_tbl">
269
+                            <tr>
270
+                                <td class="torrent_name">
271
+                                    <a href="/url">Should be the title</a>
272
+                                </td>
273
+                            </tr>
274
+                        </table>
275
+                        <table class="torrent_name_tbl">
276
+                            <tr>
277
+                                <td class="ttth">
278
+                                    <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
279
+                                    title="Télécharger des liens Magnet">[magnet]</a>
280
+                                </td>
281
+                                <td class="ttth">
282
+                                    <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
283
+                                    target="_blank" title="Ajouter à BTCloud">[cloud]</a>
284
+                                </td>
285
+                                <td>
286
+                                    <span class="attr_name">Taille:</span>
287
+                                    <span class="attr_val">1 TB</span>
288
+                                </td>
289
+                                <td>
290
+                                    <span class="attr_name">Fichiers:</span>
291
+                                    <span class="attr_val">710</span>
292
+                                </td>
293
+                                <td>
294
+                                    <span class="attr_name">Téléchargements:</span>
295
+                                    <span class="attr_val">2</span>
296
+                                </td>
297
+                                <td>
298
+                                    <span class="attr_name">Temps:</span>
299
+                                    <span class="attr_val">417.8&nbsp;jours</span>
300
+                                </td>
301
+                                <td>
302
+                                    <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
303
+                                    <span class="attr_val">5.3&nbsp;jours</span>
304
+                                </td>
305
+                                <td>
306
+                                    <span class="attr_name">Faux:</span>
307
+                                    <span class="attr_val">Aucun</span>
308
+                                </td>
309
+                            </tr>
310
+                        </table>
311
+                        <pre class="snippet">
312
+                            Content
313
+                        </pre>
314
+                    </td>
315
+                </tr>
316
+                <tr>
317
+                    <td class="idx">1</td>
318
+                    <td>
319
+                        <table class="torrent_name_tbl">
320
+                            <tr>
321
+                                <td class="torrent_name">
322
+                                    <a href="/url">Should be the title</a>
323
+                                </td>
324
+                            </tr>
325
+                        </table>
326
+                        <table class="torrent_name_tbl">
327
+                            <tr>
328
+                                <td class="ttth">
329
+                                    <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
330
+                                    title="Télécharger des liens Magnet">[magnet]</a>
331
+                                </td>
332
+                                <td class="ttth">
333
+                                    <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
334
+                                    target="_blank" title="Ajouter à BTCloud">[cloud]</a>
335
+                                </td>
336
+                                <td>
337
+                                    <span class="attr_name">Taille:</span>
338
+                                    <span class="attr_val">a TB</span>
339
+                                </td>
340
+                                <td>
341
+                                    <span class="attr_name">Fichiers:</span>
342
+                                    <span class="attr_val">710</span>
343
+                                </td>
344
+                                <td>
345
+                                    <span class="attr_name">Téléchargements:</span>
346
+                                    <span class="attr_val">z</span>
347
+                                </td>
348
+                                <td>
349
+                                    <span class="attr_name">Temps:</span>
350
+                                    <span class="attr_val">417.8&nbsp;jours</span>
351
+                                </td>
352
+                                <td>
353
+                                    <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
354
+                                    <span class="attr_val">5.3&nbsp;jours</span>
355
+                                </td>
356
+                                <td>
357
+                                    <span class="attr_name">Faux:</span>
358
+                                    <span class="attr_val">Aucun</span>
359
+                                </td>
360
+                            </tr>
361
+                        </table>
362
+                        <pre class="snippet">
363
+                            Content
364
+                        </pre>
365
+                    </td>
366
+                </tr>
367
+            </table>
368
+        </div>
369
+        """
370
+        response = mock.Mock(text=html)
371
+        results = btdigg.response(response)
372
+        self.assertEqual(type(results), list)
373
+        self.assertEqual(len(results), 5)
374
+        self.assertEqual(results[0]['title'], 'Should be the title')
375
+        self.assertEqual(results[0]['url'], 'https://btdigg.org/url')
376
+        self.assertEqual(results[0]['content'], 'Content')
377
+        self.assertEqual(results[0]['seed'], 5)
378
+        self.assertEqual(results[0]['leech'], 0)
379
+        self.assertEqual(results[0]['files'], 710)
380
+        self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:magnet&dn=Test')
381
+        self.assertEqual(results[0]['filesize'], 1024)
382
+        self.assertEqual(results[1]['filesize'], 1048576)
383
+        self.assertEqual(results[2]['filesize'], 1073741824)
384
+        self.assertEqual(results[3]['filesize'], 1099511627776)

+ 74
- 0
searx/tests/engines/test_dailymotion.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import dailymotion
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestDailymotionEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        dicto['language'] = 'fr_FR'
14
+        params = dailymotion.request(query, dicto)
15
+        self.assertTrue('url' in params)
16
+        self.assertTrue(query in params['url'])
17
+        self.assertTrue('dailymotion.com' in params['url'])
18
+        self.assertTrue('fr' in params['url'])
19
+
20
+        dicto['language'] = 'all'
21
+        params = dailymotion.request(query, dicto)
22
+        self.assertTrue('en' in params['url'])
23
+
24
+    def test_response(self):
25
+        self.assertRaises(AttributeError, dailymotion.response, None)
26
+        self.assertRaises(AttributeError, dailymotion.response, [])
27
+        self.assertRaises(AttributeError, dailymotion.response, '')
28
+        self.assertRaises(AttributeError, dailymotion.response, '[]')
29
+
30
+        response = mock.Mock(text='{}')
31
+        self.assertEqual(dailymotion.response(response), [])
32
+
33
+        response = mock.Mock(text='{"data": []}')
34
+        self.assertEqual(dailymotion.response(response), [])
35
+
36
+        json = """
37
+        {
38
+        "page": 1,
39
+        "limit": 5,
40
+        "explicit": false,
41
+        "total": 289487,
42
+        "has_more": true,
43
+        "list": [
44
+            {
45
+            "created_time": 1422173451,
46
+            "title": "Title",
47
+            "description": "Description",
48
+            "duration": 81,
49
+            "url": "http://www.url",
50
+            "thumbnail_360_url": "http://thumbnail",
51
+            "id": "x2fit7q"
52
+            }
53
+        ]
54
+        }
55
+        """
56
+        response = mock.Mock(text=json)
57
+        results = dailymotion.response(response)
58
+        self.assertEqual(type(results), list)
59
+        self.assertEqual(len(results), 1)
60
+        self.assertEqual(results[0]['title'], 'Title')
61
+        self.assertEqual(results[0]['url'], 'http://www.url')
62
+        self.assertEqual(results[0]['content'], 'Description')
63
+        self.assertIn('x2fit7q', results[0]['embedded'])
64
+
65
+        json = """
66
+        {"toto":[
67
+            {"id":200,"name":"Artist Name",
68
+            "link":"http:\/\/www.dailymotion.com\/artist\/1217","type":"artist"}
69
+        ]}
70
+        """
71
+        response = mock.Mock(text=json)
72
+        results = dailymotion.response(response)
73
+        self.assertEqual(type(results), list)
74
+        self.assertEqual(len(results), 0)

+ 57
- 0
searx/tests/engines/test_deezer.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import deezer
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestDeezerEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = deezer.request(query, dicto)
14
+        self.assertTrue('url' in params)
15
+        self.assertTrue(query in params['url'])
16
+        self.assertTrue('deezer.com' in params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, deezer.response, None)
20
+        self.assertRaises(AttributeError, deezer.response, [])
21
+        self.assertRaises(AttributeError, deezer.response, '')
22
+        self.assertRaises(AttributeError, deezer.response, '[]')
23
+
24
+        response = mock.Mock(text='{}')
25
+        self.assertEqual(deezer.response(response), [])
26
+
27
+        response = mock.Mock(text='{"data": []}')
28
+        self.assertEqual(deezer.response(response), [])
29
+
30
+        json = """
31
+        {"data":[
32
+            {"id":100, "title":"Title of track",
33
+            "link":"http:\/\/www.deezer.com\/track\/1094042","duration":232,
34
+            "artist":{"id":200,"name":"Artist Name",
35
+                "link":"http:\/\/www.deezer.com\/artist\/1217","type":"artist"},
36
+            "album":{"id":118106,"title":"Album Title","type":"album"},"type":"track"}
37
+        ]}
38
+        """
39
+        response = mock.Mock(text=json)
40
+        results = deezer.response(response)
41
+        self.assertEqual(type(results), list)
42
+        self.assertEqual(len(results), 1)
43
+        self.assertEqual(results[0]['title'], 'Title of track')
44
+        self.assertEqual(results[0]['url'], 'http://www.deezer.com/track/1094042')
45
+        self.assertEqual(results[0]['content'], 'Artist Name &bull; Album Title &bull; Title of track')
46
+        self.assertTrue('100' in results[0]['embedded'])
47
+
48
+        json = """
49
+        {"data":[
50
+            {"id":200,"name":"Artist Name",
51
+            "link":"http:\/\/www.deezer.com\/artist\/1217","type":"artist"}
52
+        ]}
53
+        """
54
+        response = mock.Mock(text=json)
55
+        results = deezer.response(response)
56
+        self.assertEqual(type(results), list)
57
+        self.assertEqual(len(results), 0)

+ 118
- 0
searx/tests/engines/test_deviantart.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import deviantart
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestDeviantartEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = deviantart.request(query, dicto)
14
+        self.assertTrue('url' in params)
15
+        self.assertTrue(query in params['url'])
16
+        self.assertTrue('deviantart.com' in params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, deviantart.response, None)
20
+        self.assertRaises(AttributeError, deviantart.response, [])
21
+        self.assertRaises(AttributeError, deviantart.response, '')
22
+        self.assertRaises(AttributeError, deviantart.response, '[]')
23
+
24
+        response = mock.Mock(text='<html></html>')
25
+        self.assertEqual(deviantart.response(response), [])
26
+
27
+        response = mock.Mock(status_code=302)
28
+        self.assertEqual(deviantart.response(response), [])
29
+
30
+        html = """
31
+        <div class="tt-a tt-fh tt-boxed" collect_rid="1:149167425"
32
+            usericon="http://a.deviantart.net/avatars/t/e/test-0.gif" userid="233301"
33
+            username="test-0" symbol="~" category="digitalart/animation">
34
+            <span class="tt-w" style="width: auto; max-width: 277px;">
35
+                <span class="tt-fh-tc" style="width: 202px;">
36
+                    <span class="tt-bb" style="width: 202px;">
37
+                    </span>
38
+                    <span class="shadow">
39
+                        <a class="thumb" href="http://url.of.result/2nd.part.of.url"
40
+                            title="Behoimi BE Animation Test by test-0, Jan 4,
41
+                            2010 in Digital Art &gt; Animation"> <i></i>
42
+                            <img width="200" height="200" alt="Test"
43
+                                src="http://url.of.thumbnail" data-src="http://th08.deviantart.net/test.jpg">
44
+                        </a>
45
+                    </span>
46
+                    <!-- ^TTT -->
47
+                </span>
48
+                <span class="details">
49
+                    <a href="http://test-0.deviantart.com/art/Test" class="t"
50
+                        title="Behoimi BE Animation Test by test-0, Jan 4, 2010">
51
+                        <span class="tt-fh-oe">Title of image</span> </a>
52
+                    <small>
53
+                    <span class="category">
54
+                        <span class="age">
55
+                            5 years ago
56
+                        </span>
57
+                        in <a title="Behoimi BE Animation Test by test-0, Jan 4, 2010"
58
+                            href="http://www.deviantart.com/browse/all/digitalart/animation/">Animation</a>
59
+                    </span>
60
+                    <div class="commentcount">
61
+                        <a href="http://test-0.deviantart.com/art/Test#comments">
62
+                        <span class="iconcommentsstats"></span>9 Comments</a>
63
+                    </div>
64
+                    <a class="mlt-link" href="http://www.deviantart.com/morelikethis/149167425">
65
+                    <span class="mlt-icon"></span> <span class="mlt-text">More Like This</span> </a>
66
+                </span>
67
+                </small> <!-- TTT$ -->
68
+            </span>
69
+        </div>
70
+        """
71
+        response = mock.Mock(text=html)
72
+        results = deviantart.response(response)
73
+        self.assertEqual(type(results), list)
74
+        self.assertEqual(len(results), 1)
75
+        self.assertEqual(results[0]['title'], 'Title of image')
76
+        self.assertEqual(results[0]['url'], 'http://url.of.result/2nd.part.of.url')
77
+        self.assertNotIn('content', results[0])
78
+        self.assertEqual(results[0]['thumbnail_src'], 'http://url.of.thumbnail')
79
+
80
+        html = """
81
+        <span class="tt-fh-tc" style="width: 202px;">
82
+            <span class="tt-bb" style="width: 202px;">
83
+            </span>
84
+            <span class="shadow">
85
+                <a class="thumb" href="http://url.of.result/2nd.part.of.url"
86
+                    title="Behoimi BE Animation Test by test-0, Jan 4,
87
+                    2010 in Digital Art &gt; Animation"> <i></i>
88
+                    <img width="200" height="200" alt="Test"
89
+                        src="http://url.of.thumbnail" data-src="http://th08.deviantart.net/test.jpg">
90
+                </a>
91
+            </span>
92
+            <!-- ^TTT -->
93
+        </span>
94
+        <span class="details">
95
+            <a href="http://test-0.deviantart.com/art/Test" class="t"
96
+                title="Behoimi BE Animation Test by test-0, Jan 4, 2010">
97
+                <span class="tt-fh-oe">Title of image</span> </a>
98
+            <small>
99
+            <span class="category">
100
+                <span class="age">
101
+                    5 years ago
102
+                </span>
103
+                in <a title="Behoimi BE Animation Test by test-0, Jan 4, 2010"
104
+                    href="http://www.deviantart.com/browse/all/digitalart/animation/">Animation</a>
105
+            </span>
106
+            <div class="commentcount">
107
+                <a href="http://test-0.deviantart.com/art/Test#comments">
108
+                <span class="iconcommentsstats"></span>9 Comments</a>
109
+            </div>
110
+            <a class="mlt-link" href="http://www.deviantart.com/morelikethis/149167425">
111
+            <span class="mlt-icon"></span> <span class="mlt-text">More Like This</span> </a>
112
+        </span>
113
+        </small> <!-- TTT$ -->
114
+        """
115
+        response = mock.Mock(text=html)
116
+        results = deviantart.response(response)
117
+        self.assertEqual(type(results), list)
118
+        self.assertEqual(len(results), 0)

+ 101
- 0
searx/tests/engines/test_digg.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import digg
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestDiggEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 1
13
+        params = digg.request(query, dicto)
14
+        self.assertIn('url', params)
15
+        self.assertIn(query, params['url'])
16
+        self.assertIn('digg.com', params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, digg.response, None)
20
+        self.assertRaises(AttributeError, digg.response, [])
21
+        self.assertRaises(AttributeError, digg.response, '')
22
+        self.assertRaises(AttributeError, digg.response, '[]')
23
+
24
+        response = mock.Mock(text='{}')
25
+        self.assertEqual(digg.response(response), [])
26
+
27
+        response = mock.Mock(text='{"data": []}')
28
+        self.assertEqual(digg.response(response), [])
29
+
30
+        json = """
31
+        {
32
+        "status": "ok",
33
+        "num": 10,
34
+        "next_position": 20,
35
+        "html": "<article itemscope itemtype=\\"http://schema.org/Article\\"
36
+        class=\\"story-container digg-story-el hentry entry story-1sRANah col-1\\"
37
+        data-content-id=\\"1sRANah\\" data-contenturl=\\"http://url.of.link\\"
38
+        data-position=\\"0\\" data-diggs=\\"24\\" data-tweets=\\"69\\"
39
+        data-digg-score=\\"1190\\"> <div class=\\"story-image story-image-thumb\\">
40
+        <a data-position=\\"0\\" data-content-id=\\"1sRANah\\"
41
+        class=\\"story-link\\" href=\\"http://www.thedailybeast.com/\\"
42
+        target=\\"_blank\\"><img class=\\"story-image-img\\"
43
+        src=\\"http://url.of.image.jpeg\\" width=\\"312\\" height=\\"170\\"
44
+        alt=\\"\\" /> </a> </div> <div class=\\"story-content\\"><header
45
+        class=\\"story-header\\"> <div itemprop=\\"alternativeHeadline\\"
46
+        class=\\"story-kicker\\" >Kicker</div> <h2 itemprop=\\"headline\\"
47
+        class=\\"story-title entry-title\\"><a class=\\"story-title-link story-link\\"
48
+        rel=\\"bookmark\\" itemprop=\\"url\\" href=\\"http://www.thedailybeast.com/\\"
49
+        target=\\"_blank\\">Title of article</h2> <div class=\\"story-meta\\">
50
+        <div class=\\"story-score \\">
51
+        <div class=\\"story-score-diggscore diggscore-1sRANah\\">1190</div>
52
+        <div class=\\"story-score-details\\"> <div class=\\"arrow\\"></div>
53
+        <ul class=\\"story-score-details-list\\"> <li
54
+        class=\\"story-score-detail story-score-diggs\\"><span
55
+        class=\\"label\\">Diggs:</span> <span class=\\"count diggs-1sRANah\\">24</span>
56
+        </li> <li class=\\"story-score-detail story-score-twitter\\"><span
57
+        class=\\"label\\">Tweets:</span> <span class=\\"count tweets-1sRANah\\">69</span>
58
+        </li> <li class=\\"story-score-detail story-score-facebook\\"><span
59
+        class=\\"label\\">Facebook Shares:</span> <span
60
+        class=\\"count fb_shares-1sRANah\\">1097</span></li> </ul> </div> </div>
61
+        <span class=\\"story-meta-item story-source\\"> <a
62
+        itemprop=\\"publisher copyrightHolder sourceOrganization provider\\"
63
+        class=\\"story-meta-item-link story-source-link\\"
64
+        href=\\"/source/thedailybeast.com\\">The Daily Beast </a> </span>
65
+        <span class=\\"story-meta-item story-tag first-tag\\"> <a
66
+        itemprop=\\"keywords\\" rel=\\"tag\\"
67
+        class=\\"story-meta-item-link story-tag-link\\" href=\\"/tag/news\\">News</a>
68
+        </span> <abbr class=\\"published story-meta-item story-timestamp\\"
69
+        title=\\"2014-10-18 14:53:45\\"> <time datetime=\\"2014-10-18 14:53:45\\">18 Oct 2014</time>
70
+        </abbr> </div> </header> </div> <ul class=\\"story-actions\\"> <li
71
+        class=\\"story-action story-action-digg btn-story-action-container\\">
72
+        <a class=\\"target digg-1sRANah\\" href=\\"#\\">Digg</a></li> <li
73
+        class=\\"story-action story-action-save btn-story-action-container\\">
74
+        <a class=\\"target save-1sRANah\\" href=\\"#\\">Save</a></li> <li
75
+        class=\\"story-action story-action-share\\"><a
76
+        class=\\"target share-facebook\\" href=\\"https://www.facebook.com/\\">Facebook</a></li>
77
+        <li class=\\"story-action story-action-share\\"><a class=\\"target share-twitter\\"
78
+        href=\\"https://twitter.com/\\">Twitter</a></li> </ul> </article>"
79
+        }
80
+        """
81
+        json = json.replace('\r\n', '').replace('\n', '').replace('\r', '')
82
+        response = mock.Mock(text=json)
83
+        results = digg.response(response)
84
+        self.assertEqual(type(results), list)
85
+        self.assertEqual(len(results), 1)
86
+        self.assertEqual(results[0]['title'], 'Title of article')
87
+        self.assertEqual(results[0]['url'], 'http://url.of.link')
88
+        self.assertEqual(results[0]['thumbnail'], 'http://url.of.image.jpeg')
89
+        self.assertEqual(results[0]['content'], '')
90
+
91
+        json = """
92
+        {
93
+        "status": "error",
94
+        "num": 10,
95
+        "next_position": 20
96
+        }
97
+        """
98
+        response = mock.Mock(text=json)
99
+        results = digg.response(response)
100
+        self.assertEqual(type(results), list)
101
+        self.assertEqual(len(results), 0)

+ 142
- 0
searx/tests/engines/test_flickr.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import flickr
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestFlickrEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = flickr.request(query, dicto)
14
+        self.assertTrue('url' in params)
15
+        self.assertTrue(query in params['url'])
16
+        self.assertTrue('flickr.com' in params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, flickr.response, None)
20
+        self.assertRaises(AttributeError, flickr.response, [])
21
+        self.assertRaises(AttributeError, flickr.response, '')
22
+        self.assertRaises(AttributeError, flickr.response, '[]')
23
+
24
+        response = mock.Mock(text='{}')
25
+        self.assertEqual(flickr.response(response), [])
26
+
27
+        response = mock.Mock(text='{"data": []}')
28
+        self.assertEqual(flickr.response(response), [])
29
+
30
+        json = """
31
+        { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
32
+            "photo": [
33
+            { "id": "15751017054", "owner": "66847915@N08",
34
+            "secret": "69c22afc40", "server": "7285", "farm": 8,
35
+            "title": "Photo title", "ispublic": 1,
36
+            "isfriend": 0, "isfamily": 0,
37
+            "description": { "_content": "Description" },
38
+            "ownername": "Owner",
39
+            "url_o": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_9178e0f963_o.jpg",
40
+            "height_o": "2100", "width_o": "2653",
41
+            "url_n": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_n.jpg",
42
+            "height_n": "253", "width_n": "320",
43
+            "url_z": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_z.jpg",
44
+            "height_z": "507", "width_z": "640" }
45
+        ] }, "stat": "ok" }
46
+        """
47
+        response = mock.Mock(text=json)
48
+        results = flickr.response(response)
49
+        self.assertEqual(type(results), list)
50
+        self.assertEqual(len(results), 1)
51
+        self.assertEqual(results[0]['title'], 'Photo title')
52
+        self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054')
53
+        self.assertTrue('o.jpg' in results[0]['img_src'])
54
+        self.assertTrue('n.jpg' in results[0]['thumbnail_src'])
55
+        self.assertTrue('Owner' in results[0]['content'])
56
+        self.assertTrue('Description' in results[0]['content'])
57
+
58
+        json = """
59
+        { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
60
+            "photo": [
61
+            { "id": "15751017054", "owner": "66847915@N08",
62
+            "secret": "69c22afc40", "server": "7285", "farm": 8,
63
+            "title": "Photo title", "ispublic": 1,
64
+            "isfriend": 0, "isfamily": 0,
65
+            "description": { "_content": "Description" },
66
+            "ownername": "Owner",
67
+            "url_z": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_z.jpg",
68
+            "height_z": "507", "width_z": "640" }
69
+        ] }, "stat": "ok" }
70
+        """
71
+        response = mock.Mock(text=json)
72
+        results = flickr.response(response)
73
+        self.assertEqual(type(results), list)
74
+        self.assertEqual(len(results), 1)
75
+        self.assertEqual(results[0]['title'], 'Photo title')
76
+        self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054')
77
+        self.assertTrue('z.jpg' in results[0]['img_src'])
78
+        self.assertTrue('z.jpg' in results[0]['thumbnail_src'])
79
+        self.assertTrue('Owner' in results[0]['content'])
80
+        self.assertTrue('Description' in results[0]['content'])
81
+
82
+        json = """
83
+        { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
84
+            "photo": [
85
+            { "id": "15751017054", "owner": "66847915@N08",
86
+            "secret": "69c22afc40", "server": "7285", "farm": 8,
87
+            "title": "Photo title", "ispublic": 1,
88
+            "isfriend": 0, "isfamily": 0,
89
+            "description": { "_content": "Description" },
90
+            "ownername": "Owner",
91
+            "url_o": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_9178e0f963_o.jpg",
92
+            "height_o": "2100", "width_o": "2653" }
93
+        ] }, "stat": "ok" }
94
+        """
95
+        response = mock.Mock(text=json)
96
+        results = flickr.response(response)
97
+        self.assertEqual(type(results), list)
98
+        self.assertEqual(len(results), 1)
99
+        self.assertEqual(results[0]['title'], 'Photo title')
100
+        self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054')
101
+        self.assertTrue('o.jpg' in results[0]['img_src'])
102
+        self.assertTrue('o.jpg' in results[0]['thumbnail_src'])
103
+        self.assertTrue('Owner' in results[0]['content'])
104
+        self.assertTrue('Description' in results[0]['content'])
105
+
106
+        json = """
107
+        { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
108
+            "photo": [
109
+            { "id": "15751017054", "owner": "66847915@N08",
110
+            "secret": "69c22afc40", "server": "7285", "farm": 8,
111
+            "title": "Photo title", "ispublic": 1,
112
+            "isfriend": 0, "isfamily": 0,
113
+            "description": { "_content": "Description" },
114
+            "ownername": "Owner",
115
+            "url_n": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_n.jpg",
116
+            "height_n": "253", "width_n": "320" }
117
+        ] }, "stat": "ok" }
118
+        """
119
+        response = mock.Mock(text=json)
120
+        results = flickr.response(response)
121
+        self.assertEqual(type(results), list)
122
+        self.assertEqual(len(results), 0)
123
+
124
+        json = """
125
+        { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
126
+            "toto": [] }, "stat": "ok" }
127
+        """
128
+        response = mock.Mock(text=json)
129
+        results = flickr.response(response)
130
+        self.assertEqual(type(results), list)
131
+        self.assertEqual(len(results), 0)
132
+
133
+        json = """
134
+        {"toto":[
135
+            {"id":200,"name":"Artist Name",
136
+            "link":"http:\/\/www.flickr.com\/artist\/1217","type":"artist"}
137
+        ]}
138
+        """
139
+        response = mock.Mock(text=json)
140
+        results = flickr.response(response)
141
+        self.assertEqual(type(results), list)
142
+        self.assertEqual(len(results), 0)

+ 442
- 0
searx/tests/engines/test_flickr_noapi.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import flickr_noapi
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestFlickrNoapiEngine(SearxTestCase):
8
+
9
+    def test_build_flickr_url(self):
10
+        url = flickr_noapi.build_flickr_url("uid", "pid")
11
+        self.assertIn("uid", url)
12
+        self.assertIn("pid", url)
13
+
14
+    def test_request(self):
15
+        query = 'test_query'
16
+        dicto = defaultdict(dict)
17
+        dicto['pageno'] = 1
18
+        params = flickr_noapi.request(query, dicto)
19
+        self.assertIn('url', params)
20
+        self.assertIn(query, params['url'])
21
+        self.assertIn('flickr.com', params['url'])
22
+
23
+    def test_response(self):
24
+        self.assertRaises(AttributeError, flickr_noapi.response, None)
25
+        self.assertRaises(AttributeError, flickr_noapi.response, [])
26
+        self.assertRaises(AttributeError, flickr_noapi.response, '')
27
+        self.assertRaises(AttributeError, flickr_noapi.response, '[]')
28
+
29
+        response = mock.Mock(text='"search-photos-models","photos":{},"totalItems":')
30
+        self.assertEqual(flickr_noapi.response(response), [])
31
+
32
+        response = mock.Mock(text='search-photos-models","photos":{"data": []},"totalItems":')
33
+        self.assertEqual(flickr_noapi.response(response), [])
34
+
35
+        json = """
36
+        "search-photos-models","photos":
37
+        {
38
+          "_data": [
39
+            {
40
+              "_flickrModelRegistry": "photo-models",
41
+              "title": "This is the title",
42
+              "sizes": {
43
+                "c": {
44
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_c.jpg",
45
+                  "width": 541,
46
+                  "height": 800,
47
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_c.jpg",
48
+                  "key": "c"
49
+                },
50
+                "h": {
51
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_761d32237a_h.jpg",
52
+                  "width": 1081,
53
+                  "height": 1600,
54
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_761d32237a_h.jpg",
55
+                  "key": "h"
56
+                },
57
+                "k": {
58
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_f145a2c11a_k.jpg",
59
+                  "width": 1383,
60
+                  "height": 2048,
61
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_f145a2c11a_k.jpg",
62
+                  "key": "k"
63
+                },
64
+                "l": {
65
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_b.jpg",
66
+                  "width": 692,
67
+                  "height": 1024,
68
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_b.jpg",
69
+                  "key": "l"
70
+                },
71
+                "m": {
72
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777.jpg",
73
+                  "width": 338,
74
+                  "height": 500,
75
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777.jpg",
76
+                  "key": "m"
77
+                },
78
+                "n": {
79
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_n.jpg",
80
+                  "width": 216,
81
+                  "height": 320,
82
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_n.jpg",
83
+                  "key": "n"
84
+                },
85
+                "q": {
86
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_q.jpg",
87
+                  "width": 150,
88
+                  "height": 150,
89
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_q.jpg",
90
+                  "key": "q"
91
+                },
92
+                "s": {
93
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_m.jpg",
94
+                  "width": 162,
95
+                  "height": 240,
96
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_m.jpg",
97
+                  "key": "s"
98
+                },
99
+                "sq": {
100
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_s.jpg",
101
+                  "width": 75,
102
+                  "height": 75,
103
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_s.jpg",
104
+                  "key": "sq"
105
+                },
106
+                "t": {
107
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_t.jpg",
108
+                  "width": 68,
109
+                  "height": 100,
110
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_t.jpg",
111
+                  "key": "t"
112
+                },
113
+                "z": {
114
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_z.jpg",
115
+                  "width": 433,
116
+                  "height": 640,
117
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_z.jpg",
118
+                  "key": "z"
119
+                }
120
+              },
121
+              "canComment": false,
122
+              "rotation": 0,
123
+              "owner": {
124
+                "_flickrModelRegistry": "person-models",
125
+                "pathAlias": "klink692",
126
+                "username": "Owner",
127
+                "buddyicon": {
128
+                  "retina": null,
129
+                  "large": null,
130
+                  "medium": null,
131
+                  "small": null,
132
+                  "default": "//c1.staticflickr.com/9/8108/buddyicons/59729010@N00.jpg?1361642376#59729010@N00"
133
+                },
134
+                "isPro": true,
135
+                "id": "59729010@N00"
136
+              },
137
+              "engagement": {
138
+                "_flickrModelRegistry": "photo-engagement-models",
139
+                "ownerNsid": "59729010@N00",
140
+                "faveCount": 21,
141
+                "commentCount": 14,
142
+                "viewCount": 10160,
143
+                "id": "14001294434"
144
+              },
145
+              "description": "Description",
146
+              "isHD": false,
147
+              "secret": "410f653777",
148
+              "canAddMeta": false,
149
+              "license": 0,
150
+              "oWidth": 1803,
151
+              "oHeight": 2669,
152
+              "safetyLevel": 0,
153
+              "id": "14001294434"
154
+            }
155
+          ],
156
+          "fetchedStart": true,
157
+          "fetchedEnd": false,
158
+          "totalItems": "4386039"
159
+        },"totalItems":
160
+        """
161
+        json = json.replace('\r\n', '').replace('\n', '').replace('\r', '')
162
+        response = mock.Mock(text=json)
163
+        results = flickr_noapi.response(response)
164
+        self.assertEqual(type(results), list)
165
+        self.assertEqual(len(results), 1)
166
+        self.assertEqual(results[0]['title'], 'This is the title')
167
+        self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434')
168
+        self.assertIn('k.jpg', results[0]['img_src'])
169
+        self.assertIn('n.jpg', results[0]['thumbnail_src'])
170
+        self.assertIn('Owner', results[0]['content'])
171
+        self.assertIn('Description', results[0]['content'])
172
+
173
+        json = """
174
+        "search-photos-models","photos":
175
+        {
176
+          "_data": [
177
+            {
178
+              "_flickrModelRegistry": "photo-models",
179
+              "title": "This is the title",
180
+              "sizes": {
181
+                "z": {
182
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_z.jpg",
183
+                  "width": 433,
184
+                  "height": 640,
185
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_z.jpg",
186
+                  "key": "z"
187
+                }
188
+              },
189
+              "canComment": false,
190
+              "rotation": 0,
191
+              "owner": {
192
+                "_flickrModelRegistry": "person-models",
193
+                "pathAlias": "klink692",
194
+                "username": "Owner",
195
+                "buddyicon": {
196
+                  "retina": null,
197
+                  "large": null,
198
+                  "medium": null,
199
+                  "small": null,
200
+                  "default": "//c1.staticflickr.com/9/8108/buddyicons/59729010@N00.jpg?1361642376#59729010@N00"
201
+                },
202
+                "isPro": true,
203
+                "id": "59729010@N00"
204
+              },
205
+              "engagement": {
206
+                "_flickrModelRegistry": "photo-engagement-models",
207
+                "ownerNsid": "59729010@N00",
208
+                "faveCount": 21,
209
+                "commentCount": 14,
210
+                "viewCount": 10160,
211
+                "id": "14001294434"
212
+              },
213
+              "description": "Description",
214
+              "isHD": false,
215
+              "secret": "410f653777",
216
+              "canAddMeta": false,
217
+              "license": 0,
218
+              "oWidth": 1803,
219
+              "oHeight": 2669,
220
+              "safetyLevel": 0,
221
+              "id": "14001294434"
222
+            }
223
+          ],
224
+          "fetchedStart": true,
225
+          "fetchedEnd": false,
226
+          "totalItems": "4386039"
227
+        },"totalItems":
228
+        """
229
+        response = mock.Mock(text=json)
230
+        results = flickr_noapi.response(response)
231
+        self.assertEqual(type(results), list)
232
+        self.assertEqual(len(results), 1)
233
+        self.assertEqual(results[0]['title'], 'This is the title')
234
+        self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434')
235
+        self.assertIn('z.jpg', results[0]['img_src'])
236
+        self.assertIn('z.jpg', results[0]['thumbnail_src'])
237
+        self.assertIn('Owner', results[0]['content'])
238
+        self.assertIn('Description', results[0]['content'])
239
+
240
+        json = """
241
+        "search-photos-models","photos":
242
+        {
243
+          "_data": [
244
+            {
245
+              "_flickrModelRegistry": "photo-models",
246
+              "title": "This is the title",
247
+              "sizes": {
248
+                "o": {
249
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_o.jpg",
250
+                  "width": 433,
251
+                  "height": 640,
252
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_o.jpg",
253
+                  "key": "o"
254
+                }
255
+              },
256
+              "canComment": false,
257
+              "rotation": 0,
258
+              "owner": {
259
+                "_flickrModelRegistry": "person-models",
260
+                "pathAlias": "klink692",
261
+                "username": "Owner",
262
+                "buddyicon": {
263
+                  "retina": null,
264
+                  "large": null,
265
+                  "medium": null,
266
+                  "small": null,
267
+                  "default": "//c1.staticflickr.com/9/8108/buddyicons/59729010@N00.jpg?1361642376#59729010@N00"
268
+                },
269
+                "isPro": true,
270
+                "id": "59729010@N00"
271
+              },
272
+              "engagement": {
273
+                "_flickrModelRegistry": "photo-engagement-models",
274
+                "ownerNsid": "59729010@N00",
275
+                "faveCount": 21,
276
+                "commentCount": 14,
277
+                "viewCount": 10160,
278
+                "id": "14001294434"
279
+              },
280
+              "isHD": false,
281
+              "secret": "410f653777",
282
+              "canAddMeta": false,
283
+              "license": 0,
284
+              "oWidth": 1803,
285
+              "oHeight": 2669,
286
+              "safetyLevel": 0,
287
+              "id": "14001294434"
288
+            }
289
+          ],
290
+          "fetchedStart": true,
291
+          "fetchedEnd": false,
292
+          "totalItems": "4386039"
293
+        },"totalItems":
294
+        """
295
+        response = mock.Mock(text=json)
296
+        results = flickr_noapi.response(response)
297
+        self.assertEqual(type(results), list)
298
+        self.assertEqual(len(results), 1)
299
+        self.assertEqual(results[0]['title'], 'This is the title')
300
+        self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434')
301
+        self.assertIn('o.jpg', results[0]['img_src'])
302
+        self.assertIn('o.jpg', results[0]['thumbnail_src'])
303
+        self.assertIn('Owner', results[0]['content'])
304
+
305
+        json = """
306
+        "search-photos-models","photos":
307
+        {
308
+          "_data": [
309
+            {
310
+              "_flickrModelRegistry": "photo-models",
311
+              "title": "This is the title",
312
+              "sizes": {
313
+              },
314
+              "canComment": false,
315
+              "rotation": 0,
316
+              "owner": {
317
+                "_flickrModelRegistry": "person-models",
318
+                "pathAlias": "klink692",
319
+                "username": "Owner",
320
+                "buddyicon": {
321
+                  "retina": null,
322
+                  "large": null,
323
+                  "medium": null,
324
+                  "small": null,
325
+                  "default": "//c1.staticflickr.com/9/8108/buddyicons/59729010@N00.jpg?1361642376#59729010@N00"
326
+                },
327
+                "isPro": true,
328
+                "id": "59729010@N00"
329
+              },
330
+              "engagement": {
331
+                "_flickrModelRegistry": "photo-engagement-models",
332
+                "ownerNsid": "59729010@N00",
333
+                "faveCount": 21,
334
+                "commentCount": 14,
335
+                "viewCount": 10160,
336
+                "id": "14001294434"
337
+              },
338
+              "description": "Description",
339
+              "isHD": false,
340
+              "secret": "410f653777",
341
+              "canAddMeta": false,
342
+              "license": 0,
343
+              "oWidth": 1803,
344
+              "oHeight": 2669,
345
+              "safetyLevel": 0,
346
+              "id": "14001294434"
347
+            }
348
+          ],
349
+          "fetchedStart": true,
350
+          "fetchedEnd": false,
351
+          "totalItems": "4386039"
352
+        },"totalItems":
353
+        """
354
+        response = mock.Mock(text=json)
355
+        results = flickr_noapi.response(response)
356
+        self.assertEqual(type(results), list)
357
+        self.assertEqual(len(results), 0)
358
+
359
+        json = """
360
+        "search-photos-models","photos":
361
+        {
362
+          "_data": [null],
363
+          "fetchedStart": true,
364
+          "fetchedEnd": false,
365
+          "totalItems": "4386039"
366
+        },"totalItems":
367
+        """
368
+        response = mock.Mock(text=json)
369
+        results = flickr_noapi.response(response)
370
+        self.assertEqual(type(results), list)
371
+        self.assertEqual(len(results), 0)
372
+
373
+        json = """
374
+        "search-photos-models","photos":
375
+        {
376
+          "_data": [
377
+            {
378
+              "_flickrModelRegistry": "photo-models",
379
+              "title": "This is the title",
380
+              "sizes": {
381
+                "o": {
382
+                  "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_o.jpg",
383
+                  "width": 433,
384
+                  "height": 640,
385
+                  "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_o.jpg",
386
+                  "key": "o"
387
+                }
388
+              },
389
+              "canComment": false,
390
+              "rotation": 0,
391
+              "owner": {
392
+                "_flickrModelRegistry": "person-models",
393
+                "pathAlias": "klink692",
394
+                "username": "Owner",
395
+                "buddyicon": {
396
+                  "retina": null,
397
+                  "large": null,
398
+                  "medium": null,
399
+                  "small": null,
400
+                  "default": "//c1.staticflickr.com/9/8108/buddyicons/59729010@N00.jpg?1361642376#59729010@N00"
401
+                },
402
+                "isPro": true
403
+              },
404
+              "engagement": {
405
+                "_flickrModelRegistry": "photo-engagement-models",
406
+                "ownerNsid": "59729010@N00",
407
+                "faveCount": 21,
408
+                "commentCount": 14,
409
+                "viewCount": 10160,
410
+                "id": "14001294434"
411
+              },
412
+              "description": "Description",
413
+              "isHD": false,
414
+              "secret": "410f653777",
415
+              "canAddMeta": false,
416
+              "license": 0,
417
+              "oWidth": 1803,
418
+              "oHeight": 2669,
419
+              "safetyLevel": 0,
420
+              "id": "14001294434"
421
+            }
422
+          ],
423
+          "fetchedStart": true,
424
+          "fetchedEnd": false,
425
+          "totalItems": "4386039"
426
+        },"totalItems":
427
+        """
428
+        response = mock.Mock(text=json)
429
+        results = flickr_noapi.response(response)
430
+        self.assertEqual(type(results), list)
431
+        self.assertEqual(len(results), 0)
432
+
433
+        json = """
434
+        {"toto":[
435
+            {"id":200,"name":"Artist Name",
436
+            "link":"http:\/\/www.flickr.com\/artist\/1217","type":"artist"}
437
+        ]}
438
+        """
439
+        response = mock.Mock(text=json)
440
+        results = flickr_noapi.response(response)
441
+        self.assertEqual(type(results), list)
442
+        self.assertEqual(len(results), 0)

+ 108
- 0
searx/tests/engines/test_google_images.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import google_images
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestGoogleImagesEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 1
13
+        params = google_images.request(query, dicto)
14
+        self.assertTrue('url' in params)
15
+        self.assertTrue(query in params['url'])
16
+        self.assertTrue('googleapis.com' in params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, google_images.response, None)
20
+        self.assertRaises(AttributeError, google_images.response, [])
21
+        self.assertRaises(AttributeError, google_images.response, '')
22
+        self.assertRaises(AttributeError, google_images.response, '[]')
23
+
24
+        response = mock.Mock(text='{}')
25
+        self.assertEqual(google_images.response(response), [])
26
+
27
+        response = mock.Mock(text='{"data": []}')
28
+        self.assertEqual(google_images.response(response), [])
29
+
30
+        json = """
31
+        {
32
+        "responseData": {
33
+            "results": [
34
+            {
35
+                "GsearchResultClass": "GimageSearch",
36
+                "width": "400",
37
+                "height": "400",
38
+                "imageId": "ANd9GcQbYb9FJuAbG_hT4i8FeC0O0x-P--EHdzgRIF9ao97nHLl7C2mREn6qTQ",
39
+                "tbWidth": "124",
40
+                "tbHeight": "124",
41
+                "unescapedUrl": "http://unescaped.url.jpg",
42
+                "url": "http://image.url.jpg",
43
+                "visibleUrl": "insolitebuzz.fr",
44
+                "title": "This is the title",
45
+                "titleNoFormatting": "Petit test sympa qui rend fou tout le monde ! A faire",
46
+                "originalContextUrl": "http://this.is.the.url",
47
+                "content": "<b>test</b>",
48
+                "contentNoFormatting": "test",
49
+                "tbUrl": "http://thumbnail.url"
50
+            }
51
+            ]
52
+        },
53
+        "responseDetails": null,
54
+        "responseStatus": 200
55
+        }
56
+        """
57
+        response = mock.Mock(text=json)
58
+        results = google_images.response(response)
59
+        self.assertEqual(type(results), list)
60
+        self.assertEqual(len(results), 1)
61
+        self.assertEqual(results[0]['title'], 'This is the title')
62
+        self.assertEqual(results[0]['url'], 'http://this.is.the.url')
63
+        self.assertEqual(results[0]['thumbnail_src'], 'http://thumbnail.url')
64
+        self.assertEqual(results[0]['img_src'], 'http://image.url.jpg')
65
+        self.assertEqual(results[0]['content'], '<b>test</b>')
66
+
67
+        json = """
68
+        {
69
+        "responseData": {
70
+            "results": [
71
+            {
72
+                "GsearchResultClass": "GimageSearch",
73
+                "width": "400",
74
+                "height": "400",
75
+                "imageId": "ANd9GcQbYb9FJuAbG_hT4i8FeC0O0x-P--EHdzgRIF9ao97nHLl7C2mREn6qTQ",
76
+                "tbWidth": "124",
77
+                "tbHeight": "124",
78
+                "unescapedUrl": "http://unescaped.url.jpg",
79
+                "visibleUrl": "insolitebuzz.fr",
80
+                "title": "This is the title",
81
+                "titleNoFormatting": "Petit test sympa qui rend fou tout le monde ! A faire",
82
+                "originalContextUrl": "http://this.is.the.url",
83
+                "content": "<b>test</b>",
84
+                "contentNoFormatting": "test",
85
+                "tbUrl": "http://thumbnail.url"
86
+            }
87
+            ]
88
+        },
89
+        "responseDetails": null,
90
+        "responseStatus": 200
91
+        }
92
+        """
93
+        response = mock.Mock(text=json)
94
+        results = google_images.response(response)
95
+        self.assertEqual(type(results), list)
96
+        self.assertEqual(len(results), 0)
97
+
98
+        json = """
99
+        {
100
+        "responseData": {},
101
+        "responseDetails": null,
102
+        "responseStatus": 200
103
+        }
104
+        """
105
+        response = mock.Mock(text=json)
106
+        results = google_images.response(response)
107
+        self.assertEqual(type(results), list)
108
+        self.assertEqual(len(results), 0)

+ 136
- 0
searx/tests/engines/test_google_news.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import google_news
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestGoogleNewsEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 1
13
+        dicto['language'] = 'fr_FR'
14
+        params = google_news.request(query, dicto)
15
+        self.assertIn('url', params)
16
+        self.assertIn(query, params['url'])
17
+        self.assertIn('googleapis.com', params['url'])
18
+        self.assertIn('fr', params['url'])
19
+
20
+        dicto['language'] = 'all'
21
+        params = google_news.request(query, dicto)
22
+        self.assertIn('url', params)
23
+        self.assertIn('en', params['url'])
24
+
25
+    def test_response(self):
26
+        self.assertRaises(AttributeError, google_news.response, None)
27
+        self.assertRaises(AttributeError, google_news.response, [])
28
+        self.assertRaises(AttributeError, google_news.response, '')
29
+        self.assertRaises(AttributeError, google_news.response, '[]')
30
+
31
+        response = mock.Mock(text='{}')
32
+        self.assertEqual(google_news.response(response), [])
33
+
34
+        response = mock.Mock(text='{"data": []}')
35
+        self.assertEqual(google_news.response(response), [])
36
+
37
+        json = """
38
+        {
39
+        "responseData": {
40
+            "results": [
41
+            {
42
+                "GsearchResultClass": "GnewsSearch",
43
+                "clusterUrl": "http://news.google.com/news/story?ncl=d2d3t1LMDpNIj2MPPhdTT0ycN4sWM&hl=fr&ned=fr",
44
+                "content": "This is the content",
45
+                "unescapedUrl": "http://this.is.the.url",
46
+                "url": "http://this.is.the.url",
47
+                "title": "This is the title",
48
+                "titleNoFormatting": "This is the title",
49
+                "location": "",
50
+                "publisher": "Jeux Actu",
51
+                "publishedDate": "Fri, 30 Jan 2015 11:00:25 -0800",
52
+                "signedRedirectUrl": "http://news.google.com/",
53
+                "language": "fr",
54
+                "image": {
55
+                "url": "http://i.jeuxactus.com/datas/jeux/d/y/dying-light/vu/dying-light-54cc080b568fb.jpg",
56
+                "tbUrl": "http://t1.gstatic.com/images?q=tbn:ANd9GcSF4yYrs9Ycw23DGiOSAZ-5SEPXYwG3LNs",
57
+                "originalContextUrl": "http://www.jeuxactu.com/test-dying-light-sur-ps4-97208.htm",
58
+                "publisher": "Jeux Actu",
59
+                "tbWidth": 80,
60
+                "tbHeight": 30
61
+                },
62
+                "relatedStories": [
63
+                {
64
+                    "unescapedUrl": "http://www.jeuxvideo.com/test/415823/dying-light.htm",
65
+                    "url": "http%3A%2F%2Fwww.jeuxvideo.com%2Ftest%2F415823%2Fdying-light.htm",
66
+                    "title": "<b>Test</b> du jeu Dying Light - jeuxvideo.com",
67
+                    "titleNoFormatting": "Test du jeu Dying Light - jeuxvideo.com",
68
+                    "location": "",
69
+                    "publisher": "JeuxVideo.com",
70
+                    "publishedDate": "Fri, 30 Jan 2015 08:52:30 -0800",
71
+                    "signedRedirectUrl": "http://news.google.com/news/url?sa=T&",
72
+                    "language": "fr"
73
+                }
74
+                ]
75
+            }
76
+            ]
77
+        },
78
+        "responseDetails": null,
79
+        "responseStatus": 200
80
+        }
81
+        """
82
+        response = mock.Mock(text=json)
83
+        results = google_news.response(response)
84
+        self.assertEqual(type(results), list)
85
+        self.assertEqual(len(results), 1)
86
+        self.assertEqual(results[0]['title'], 'This is the title')
87
+        self.assertEqual(results[0]['url'], 'http://this.is.the.url')
88
+        self.assertEqual(results[0]['content'], 'This is the content')
89
+
90
+        json = """
91
+        {
92
+        "responseData": {
93
+            "results": [
94
+            {
95
+                "GsearchResultClass": "GnewsSearch",
96
+                "clusterUrl": "http://news.google.com/news/story?ncl=d2d3t1LMDpNIj2MPPhdTT0ycN4sWM&hl=fr&ned=fr",
97
+                "content": "This is the content",
98
+                "unescapedUrl": "http://this.is.the.url",
99
+                "title": "This is the title",
100
+                "titleNoFormatting": "This is the title",
101
+                "location": "",
102
+                "publisher": "Jeux Actu",
103
+                "publishedDate": "Fri, 30 Jan 2015 11:00:25 -0800",
104
+                "signedRedirectUrl": "http://news.google.com/news/",
105
+                "language": "fr",
106
+                "image": {
107
+                "url": "http://i.jeuxactus.com/datas/jeux/d/y/dying-light/vu/dying-light-54cc080b568fb.jpg",
108
+                "tbUrl": "http://t1.gstatic.com/images?q=tbn:b_6f-OSAZ-5SEPXYwG3LNs",
109
+                "originalContextUrl": "http://www.jeuxactu.com/test-dying-light-sur-ps4-97208.htm",
110
+                "publisher": "Jeux Actu",
111
+                "tbWidth": 80,
112
+                "tbHeight": 30
113
+                }
114
+            }
115
+            ]
116
+        },
117
+        "responseDetails": null,
118
+        "responseStatus": 200
119
+        }
120
+        """
121
+        response = mock.Mock(text=json)
122
+        results = google_news.response(response)
123
+        self.assertEqual(type(results), list)
124
+        self.assertEqual(len(results), 0)
125
+
126
+        json = """
127
+        {
128
+        "responseData": {},
129
+        "responseDetails": null,
130
+        "responseStatus": 200
131
+        }
132
+        """
133
+        response = mock.Mock(text=json)
134
+        results = google_news.response(response)
135
+        self.assertEqual(type(results), list)
136
+        self.assertEqual(len(results), 0)

+ 398
- 0
searx/tests/engines/test_kickass.py View File

1
+# -*- coding: utf-8 -*-
2
+from collections import defaultdict
3
+import mock
4
+from searx.engines import kickass
5
+from searx.testing import SearxTestCase
6
+
7
+
8
+class TestKickassEngine(SearxTestCase):
9
+
10
+    def test_request(self):
11
+        query = 'test_query'
12
+        dicto = defaultdict(dict)
13
+        dicto['pageno'] = 1
14
+        params = kickass.request(query, dicto)
15
+        self.assertIn('url', params)
16
+        self.assertIn(query, params['url'])
17
+        self.assertIn('kickass.so', params['url'])
18
+        self.assertIn('verify', params)
19
+        self.assertFalse(params['verify'])
20
+
21
+    def test_response(self):
22
+        self.assertRaises(AttributeError, kickass.response, None)
23
+        self.assertRaises(AttributeError, kickass.response, [])
24
+        self.assertRaises(AttributeError, kickass.response, '')
25
+        self.assertRaises(AttributeError, kickass.response, '[]')
26
+
27
+        response = mock.Mock(text='<html></html>')
28
+        self.assertEqual(kickass.response(response), [])
29
+
30
+        html = """
31
+        <table cellpadding="0" cellspacing="0" class="data" style="width: 100%">
32
+            <tr class="firstr">
33
+                <th class="width100perc nopad">torrent name</th>
34
+                <th class="center">
35
+                    <a href="/search/test/?field=size&sorder=desc" rel="nofollow">size</a>
36
+                </th>
37
+                <th class="center"><span class="files">
38
+                    <a href="/search/test/?field=files_count&sorder=desc" rel="nofollow">files</a></span>
39
+                </th>
40
+                <th class="center"><span>
41
+                    <a href="/search/test/?field=time_add&sorder=desc" rel="nofollow">age</a></span>
42
+                </th>
43
+                <th class="center"><span class="seed">
44
+                    <a href="/search/test/?field=seeders&sorder=desc" rel="nofollow">seed</a></span>
45
+                </th>
46
+                <th class="lasttd nobr center">
47
+                    <a href="/search/test/?field=leechers&sorder=desc" rel="nofollow">leech</a>
48
+                </th>
49
+            </tr>
50
+            <tr class="even" id="torrent_test6478745">
51
+                <td>
52
+                    <div class="iaconbox center floatright">
53
+                        <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
54
+                            <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
55
+                            <i class="ka ka-comment"></i>
56
+                        </a>
57
+                        <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
58
+                            <i class="ka ka16 ka-verify ka-green"></i>
59
+                        </a>
60
+                        <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
61
+                            <i class="ka ka16 ka-arrow-down partner1Button"></i>
62
+                        </a>
63
+                        <a title="Torrent magnet link"
64
+                            href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
65
+                            <i class="ka ka16 ka-magnet"></i>
66
+                        </a>
67
+                        <a title="Download torrent file"
68
+                            href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
69
+                            <i class="ka ka16 ka-arrow-down"></i>
70
+                        </a>
71
+                    </div>
72
+                    <div class="torrentname">
73
+                    <a href="/test-t6478745.html" class="torType txtType"></a>
74
+                    <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
75
+                    <div class="markeredBlock torType txtType">
76
+                        <a href="/url.html" class="cellMainLink">
77
+                            <strong class="red">This should be the title</strong>
78
+                        </a>
79
+                        <span class="font11px lightgrey block">
80
+                            Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
81
+                            <a class="plain" href="/user/riri/">riri</a> in
82
+                            <span id="cat_6478745">
83
+                                <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
84
+                            </span>
85
+                        </span>
86
+                    </div>
87
+                </td>
88
+                <td class="nobr center">449 <span>bytes</span></td>
89
+                <td class="center">4</td>
90
+                <td class="center">2&nbsp;years</td>
91
+                <td class="green center">10</td>
92
+                <td class="red lasttd center">1</td>
93
+            </tr>
94
+        </table>
95
+        """
96
+        response = mock.Mock(text=html)
97
+        results = kickass.response(response)
98
+        self.assertEqual(type(results), list)
99
+        self.assertEqual(len(results), 1)
100
+        self.assertEqual(results[0]['title'], 'This should be the title')
101
+        self.assertEqual(results[0]['url'], 'https://kickass.so/url.html')
102
+        self.assertEqual(results[0]['content'], 'Posted by riri in Other &gt; Unsorted')
103
+        self.assertEqual(results[0]['seed'], 10)
104
+        self.assertEqual(results[0]['leech'], 1)
105
+        self.assertEqual(results[0]['filesize'], 449)
106
+        self.assertEqual(results[0]['files'], 4)
107
+        self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETURL&dn=test')
108
+        self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/53917.torrent?title=test')
109
+
110
+        html = """
111
+        <table cellpadding="0" cellspacing="0" class="data" style="width: 100%">
112
+            <tr class="firstr">
113
+                <th class="width100perc nopad">torrent name</th>
114
+                <th class="center">
115
+                    <a href="/search/test/?field=size&sorder=desc" rel="nofollow">size</a>
116
+                </th>
117
+                <th class="center"><span class="files">
118
+                    <a href="/search/test/?field=files_count&sorder=desc" rel="nofollow">files</a></span>
119
+                </th>
120
+                <th class="center"><span>
121
+                    <a href="/search/test/?field=time_add&sorder=desc" rel="nofollow">age</a></span>
122
+                </th>
123
+                <th class="center"><span class="seed">
124
+                    <a href="/search/test/?field=seeders&sorder=desc" rel="nofollow">seed</a></span>
125
+                </th>
126
+                <th class="lasttd nobr center">
127
+                    <a href="/search/test/?field=leechers&sorder=desc" rel="nofollow">leech</a>
128
+                </th>
129
+            </tr>
130
+        </table>
131
+        """
132
+        response = mock.Mock(text=html)
133
+        results = kickass.response(response)
134
+        self.assertEqual(type(results), list)
135
+        self.assertEqual(len(results), 0)
136
+
137
+        html = """
138
+        <table cellpadding="0" cellspacing="0" class="data" style="width: 100%">
139
+            <tr class="firstr">
140
+                <th class="width100perc nopad">torrent name</th>
141
+                <th class="center">
142
+                    <a href="/search/test/?field=size&sorder=desc" rel="nofollow">size</a>
143
+                </th>
144
+                <th class="center"><span class="files">
145
+                    <a href="/search/test/?field=files_count&sorder=desc" rel="nofollow">files</a></span>
146
+                </th>
147
+                <th class="center"><span>
148
+                    <a href="/search/test/?field=time_add&sorder=desc" rel="nofollow">age</a></span>
149
+                </th>
150
+                <th class="center"><span class="seed">
151
+                    <a href="/search/test/?field=seeders&sorder=desc" rel="nofollow">seed</a></span>
152
+                </th>
153
+                <th class="lasttd nobr center">
154
+                    <a href="/search/test/?field=leechers&sorder=desc" rel="nofollow">leech</a>
155
+                </th>
156
+            </tr>
157
+            <tr class="even" id="torrent_test6478745">
158
+                <td>
159
+                    <div class="iaconbox center floatright">
160
+                        <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
161
+                            <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
162
+                            <i class="ka ka-comment"></i>
163
+                        </a>
164
+                        <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
165
+                            <i class="ka ka16 ka-verify ka-green"></i>
166
+                        </a>
167
+                        <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
168
+                            <i class="ka ka16 ka-arrow-down partner1Button"></i>
169
+                        </a>
170
+                        <a title="Torrent magnet link"
171
+                            href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
172
+                            <i class="ka ka16 ka-magnet"></i>
173
+                        </a>
174
+                        <a title="Download torrent file"
175
+                            href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
176
+                            <i class="ka ka16 ka-arrow-down"></i>
177
+                        </a>
178
+                    </div>
179
+                    <div class="torrentname">
180
+                    <a href="/test-t6478745.html" class="torType txtType"></a>
181
+                    <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
182
+                    <div class="markeredBlock torType txtType">
183
+                        <a href="/url.html" class="cellMainLink">
184
+                            <strong class="red">This should be the title</strong>
185
+                        </a>
186
+                        <span class="font11px lightgrey block">
187
+                            Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
188
+                            <a class="plain" href="/user/riri/">riri</a> in
189
+                            <span id="cat_6478745">
190
+                                <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
191
+                            </span>
192
+                        </span>
193
+                    </div>
194
+                </td>
195
+                <td class="nobr center">1 <span>KB</span></td>
196
+                <td class="center">4</td>
197
+                <td class="center">2&nbsp;years</td>
198
+                <td class="green center">10</td>
199
+                <td class="red lasttd center">1</td>
200
+            </tr>
201
+            <tr class="even" id="torrent_test6478745">
202
+                <td>
203
+                    <div class="iaconbox center floatright">
204
+                        <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
205
+                            <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
206
+                            <i class="ka ka-comment"></i>
207
+                        </a>
208
+                        <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
209
+                            <i class="ka ka16 ka-verify ka-green"></i>
210
+                        </a>
211
+                        <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
212
+                            <i class="ka ka16 ka-arrow-down partner1Button"></i>
213
+                        </a>
214
+                        <a title="Torrent magnet link"
215
+                            href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
216
+                            <i class="ka ka16 ka-magnet"></i>
217
+                        </a>
218
+                        <a title="Download torrent file"
219
+                            href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
220
+                            <i class="ka ka16 ka-arrow-down"></i>
221
+                        </a>
222
+                    </div>
223
+                    <div class="torrentname">
224
+                    <a href="/test-t6478745.html" class="torType txtType"></a>
225
+                    <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
226
+                    <div class="markeredBlock torType txtType">
227
+                        <a href="/url.html" class="cellMainLink">
228
+                            <strong class="red">This should be the title</strong>
229
+                        </a>
230
+                        <span class="font11px lightgrey block">
231
+                            Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
232
+                            <a class="plain" href="/user/riri/">riri</a> in
233
+                            <span id="cat_6478745">
234
+                                <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
235
+                            </span>
236
+                        </span>
237
+                    </div>
238
+                </td>
239
+                <td class="nobr center">1 <span>MB</span></td>
240
+                <td class="center">4</td>
241
+                <td class="center">2&nbsp;years</td>
242
+                <td class="green center">9</td>
243
+                <td class="red lasttd center">1</td>
244
+            </tr>
245
+            <tr class="even" id="torrent_test6478745">
246
+                <td>
247
+                    <div class="iaconbox center floatright">
248
+                        <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
249
+                            <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
250
+                            <i class="ka ka-comment"></i>
251
+                        </a>
252
+                        <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
253
+                            <i class="ka ka16 ka-verify ka-green"></i>
254
+                        </a>
255
+                        <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
256
+                            <i class="ka ka16 ka-arrow-down partner1Button"></i>
257
+                        </a>
258
+                        <a title="Torrent magnet link"
259
+                            href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
260
+                            <i class="ka ka16 ka-magnet"></i>
261
+                        </a>
262
+                        <a title="Download torrent file"
263
+                            href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
264
+                            <i class="ka ka16 ka-arrow-down"></i>
265
+                        </a>
266
+                    </div>
267
+                    <div class="torrentname">
268
+                    <a href="/test-t6478745.html" class="torType txtType"></a>
269
+                    <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
270
+                    <div class="markeredBlock torType txtType">
271
+                        <a href="/url.html" class="cellMainLink">
272
+                            <strong class="red">This should be the title</strong>
273
+                        </a>
274
+                        <span class="font11px lightgrey block">
275
+                            Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
276
+                            <a class="plain" href="/user/riri/">riri</a> in
277
+                            <span id="cat_6478745">
278
+                                <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
279
+                            </span>
280
+                        </span>
281
+                    </div>
282
+                </td>
283
+                <td class="nobr center">1 <span>GB</span></td>
284
+                <td class="center">4</td>
285
+                <td class="center">2&nbsp;years</td>
286
+                <td class="green center">8</td>
287
+                <td class="red lasttd center">1</td>
288
+            </tr>
289
+            <tr class="even" id="torrent_test6478745">
290
+                <td>
291
+                    <div class="iaconbox center floatright">
292
+                        <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
293
+                            <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
294
+                            <i class="ka ka-comment"></i>
295
+                        </a>
296
+                        <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
297
+                            <i class="ka ka16 ka-verify ka-green"></i>
298
+                        </a>
299
+                        <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
300
+                            <i class="ka ka16 ka-arrow-down partner1Button"></i>
301
+                        </a>
302
+                        <a title="Torrent magnet link"
303
+                            href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
304
+                            <i class="ka ka16 ka-magnet"></i>
305
+                        </a>
306
+                        <a title="Download torrent file"
307
+                            href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
308
+                            <i class="ka ka16 ka-arrow-down"></i>
309
+                        </a>
310
+                    </div>
311
+                    <div class="torrentname">
312
+                    <a href="/test-t6478745.html" class="torType txtType"></a>
313
+                    <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
314
+                    <div class="markeredBlock torType txtType">
315
+                        <a href="/url.html" class="cellMainLink">
316
+                            <strong class="red">This should be the title</strong>
317
+                        </a>
318
+                        <span class="font11px lightgrey block">
319
+                            Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
320
+                            <a class="plain" href="/user/riri/">riri</a> in
321
+                            <span id="cat_6478745">
322
+                                <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
323
+                            </span>
324
+                        </span>
325
+                    </div>
326
+                </td>
327
+                <td class="nobr center">1 <span>TB</span></td>
328
+                <td class="center">4</td>
329
+                <td class="center">2&nbsp;years</td>
330
+                <td class="green center">7</td>
331
+                <td class="red lasttd center">1</td>
332
+            </tr>
333
+            <tr class="even" id="torrent_test6478745">
334
+                <td>
335
+                    <div class="iaconbox center floatright">
336
+                        <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
337
+                            <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
338
+                            <i class="ka ka-comment"></i>
339
+                        </a>
340
+                        <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
341
+                            <i class="ka ka16 ka-verify ka-green"></i>
342
+                        </a>
343
+                        <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
344
+                            <i class="ka ka16 ka-arrow-down partner1Button"></i>
345
+                        </a>
346
+                        <a title="Torrent magnet link"
347
+                            href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
348
+                            <i class="ka ka16 ka-magnet"></i>
349
+                        </a>
350
+                        <a title="Download torrent file"
351
+                            href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
352
+                            <i class="ka ka16 ka-arrow-down"></i>
353
+                        </a>
354
+                    </div>
355
+                    <div class="torrentname">
356
+                    <a href="/test-t6478745.html" class="torType txtType"></a>
357
+                    <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
358
+                    <div class="markeredBlock torType txtType">
359
+                        <a href="/url.html" class="cellMainLink">
360
+                            <strong class="red">This should be the title</strong>
361
+                        </a>
362
+                        <span class="font11px lightgrey block">
363
+                            Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
364
+                            <a class="plain" href="/user/riri/">riri</a> in
365
+                            <span id="cat_6478745">
366
+                                <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
367
+                            </span>
368
+                        </span>
369
+                    </div>
370
+                </td>
371
+                <td class="nobr center">z <span>bytes</span></td>
372
+                <td class="center">r</td>
373
+                <td class="center">2&nbsp;years</td>
374
+                <td class="green center">a</td>
375
+                <td class="red lasttd center">t</td>
376
+            </tr>
377
+        </table>
378
+        """
379
+        response = mock.Mock(text=html)
380
+        results = kickass.response(response)
381
+        self.assertEqual(type(results), list)
382
+        self.assertEqual(len(results), 5)
383
+        self.assertEqual(results[0]['title'], 'This should be the title')
384
+        self.assertEqual(results[0]['url'], 'https://kickass.so/url.html')
385
+        self.assertEqual(results[0]['content'], 'Posted by riri in Other &gt; Unsorted')
386
+        self.assertEqual(results[0]['seed'], 10)
387
+        self.assertEqual(results[0]['leech'], 1)
388
+        self.assertEqual(results[0]['files'], 4)
389
+        self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETURL&dn=test')
390
+        self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/53917.torrent?title=test')
391
+        self.assertEqual(results[0]['filesize'], 1024)
392
+        self.assertEqual(results[1]['filesize'], 1048576)
393
+        self.assertEqual(results[2]['filesize'], 1073741824)
394
+        self.assertEqual(results[3]['filesize'], 1099511627776)
395
+        self.assertEqual(results[4]['seed'], 0)
396
+        self.assertEqual(results[4]['leech'], 0)
397
+        self.assertEqual(results[4]['files'], None)
398
+        self.assertEqual(results[4]['filesize'], None)

+ 67
- 0
searx/tests/engines/test_mixcloud.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import mixcloud
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestMixcloudEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = mixcloud.request(query, dicto)
14
+        self.assertTrue('url' in params)
15
+        self.assertTrue(query in params['url'])
16
+        self.assertTrue('mixcloud.com' in params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, mixcloud.response, None)
20
+        self.assertRaises(AttributeError, mixcloud.response, [])
21
+        self.assertRaises(AttributeError, mixcloud.response, '')
22
+        self.assertRaises(AttributeError, mixcloud.response, '[]')
23
+
24
+        response = mock.Mock(text='{}')
25
+        self.assertEqual(mixcloud.response(response), [])
26
+
27
+        response = mock.Mock(text='{"data": []}')
28
+        self.assertEqual(mixcloud.response(response), [])
29
+
30
+        json = """
31
+        {"data":[
32
+            {
33
+            "user": {
34
+                "url": "http://www.mixcloud.com/user/",
35
+                "username": "user",
36
+                "name": "User",
37
+                "key": "/user/"
38
+            },
39
+            "key": "/user/this-is-the-url/",
40
+            "created_time": "2014-11-14T13:30:02Z",
41
+            "audio_length": 3728,
42
+            "slug": "this-is-the-url",
43
+            "name": "Title of track",
44
+            "url": "http://www.mixcloud.com/user/this-is-the-url/",
45
+            "updated_time": "2014-11-14T13:14:10Z"
46
+        }
47
+        ]}
48
+        """
49
+        response = mock.Mock(text=json)
50
+        results = mixcloud.response(response)
51
+        self.assertEqual(type(results), list)
52
+        self.assertEqual(len(results), 1)
53
+        self.assertEqual(results[0]['title'], 'Title of track')
54
+        self.assertEqual(results[0]['url'], 'http://www.mixcloud.com/user/this-is-the-url/')
55
+        self.assertEqual(results[0]['content'], 'User')
56
+        self.assertTrue('http://www.mixcloud.com/user/this-is-the-url/' in results[0]['embedded'])
57
+
58
+        json = """
59
+        {"toto":[
60
+            {"id":200,"name":"Artist Name",
61
+            "link":"http:\/\/www.mixcloud.com\/artist\/1217","type":"artist"}
62
+        ]}
63
+        """
64
+        response = mock.Mock(text=json)
65
+        results = mixcloud.response(response)
66
+        self.assertEqual(type(results), list)
67
+        self.assertEqual(len(results), 0)

+ 137
- 0
searx/tests/engines/test_piratebay.py View File

1
+# -*- coding: utf-8 -*-
2
+from collections import defaultdict
3
+import mock
4
+from searx.engines import piratebay
5
+from searx.testing import SearxTestCase
6
+
7
+
8
+class TestPiratebayEngine(SearxTestCase):
9
+
10
+    def test_request(self):
11
+        query = 'test_query'
12
+        dicto = defaultdict(dict)
13
+        dicto['pageno'] = 1
14
+        dicto['category'] = 'Toto'
15
+        params = piratebay.request(query, dicto)
16
+        self.assertIn('url', params)
17
+        self.assertIn(query, params['url'])
18
+        self.assertIn('piratebay.cr', params['url'])
19
+        self.assertIn('0', params['url'])
20
+
21
+        dicto['category'] = 'music'
22
+        params = piratebay.request(query, dicto)
23
+        self.assertIn('100', params['url'])
24
+
25
+    def test_response(self):
26
+        self.assertRaises(AttributeError, piratebay.response, None)
27
+        self.assertRaises(AttributeError, piratebay.response, [])
28
+        self.assertRaises(AttributeError, piratebay.response, '')
29
+        self.assertRaises(AttributeError, piratebay.response, '[]')
30
+
31
+        response = mock.Mock(text='<html></html>')
32
+        self.assertEqual(piratebay.response(response), [])
33
+
34
+        html = """
35
+        <table id="searchResult">
36
+            <tr>
37
+            </tr>
38
+            <tr>
39
+                <td class="vertTh">
40
+                    <center>
41
+                        <a href="#" title="More from this category">Anime</a><br/>
42
+                        (<a href="#" title="More from this category">Anime</a>)
43
+                    </center>
44
+                </td>
45
+                <td>
46
+                    <div class="detName">
47
+                        <a href="/this.is.the.link" class="detLink" title="Title">
48
+                            This is the title
49
+                        </a>
50
+                    </div>
51
+                    <a href="magnet:?xt=urn:btih:MAGNETLINK" title="Download this torrent using magnet">
52
+                        <img src="/static/img/icon-magnet.gif" alt="Magnet link"/>
53
+                    </a>
54
+                    <a href="http://torcache.net/torrent/TORRENTFILE.torrent" title="Download this torrent">
55
+                        <img src="/static/img/dl.gif" class="dl" alt="Download"/>
56
+                    </a>
57
+                    <a href="/user/HorribleSubs">
58
+                        <img src="/static/img/vip.gif" alt="VIP" title="VIP" style="width:11px;" border='0'/>
59
+                    </a>
60
+                    <img src="/static/img/11x11p.png"/>
61
+                    <font class="detDesc">
62
+                        This is the content <span>and should be</span> OK
63
+                    </font>
64
+                </td>
65
+                <td align="right">13</td>
66
+                <td align="right">334</td>
67
+            </tr>
68
+        </table>
69
+        """
70
+        response = mock.Mock(text=html)
71
+        results = piratebay.response(response)
72
+        self.assertEqual(type(results), list)
73
+        self.assertEqual(len(results), 1)
74
+        self.assertEqual(results[0]['title'], 'This is the title')
75
+        self.assertEqual(results[0]['url'], 'https://thepiratebay.cr/this.is.the.link')
76
+        self.assertEqual(results[0]['content'], 'This is the content and should be OK')
77
+        self.assertEqual(results[0]['seed'], 13)
78
+        self.assertEqual(results[0]['leech'], 334)
79
+        self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETLINK')
80
+        self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/TORRENTFILE.torrent')
81
+
82
+        html = """
83
+        <table id="searchResult">
84
+            <tr>
85
+            </tr>
86
+            <tr>
87
+                <td class="vertTh">
88
+                    <center>
89
+                        <a href="#" title="More from this category">Anime</a><br/>
90
+                        (<a href="#" title="More from this category">Anime</a>)
91
+                    </center>
92
+                </td>
93
+                <td>
94
+                    <div class="detName">
95
+                        <a href="/this.is.the.link" class="detLink" title="Title">
96
+                            This is the title
97
+                        </a>
98
+                    </div>
99
+                    <a href="magnet:?xt=urn:btih:MAGNETLINK" title="Download this torrent using magnet">
100
+                        <img src="/static/img/icon-magnet.gif" alt="Magnet link"/>
101
+                    </a>
102
+                    <a href="http://torcache.net/torrent/TORRENTFILE.torrent" title="Download this torrent">
103
+                        <img src="/static/img/dl.gif" class="dl" alt="Download"/>
104
+                    </a>
105
+                    <a href="/user/HorribleSubs">
106
+                        <img src="/static/img/vip.gif" alt="VIP" title="VIP" style="width:11px;" border='0'/>
107
+                    </a>
108
+                    <img src="/static/img/11x11p.png"/>
109
+                    <font class="detDesc">
110
+                        This is the content <span>and should be</span> OK
111
+                    </font>
112
+                </td>
113
+                <td align="right">s</td>
114
+                <td align="right">d</td>
115
+            </tr>
116
+        </table>
117
+        """
118
+        response = mock.Mock(text=html)
119
+        results = piratebay.response(response)
120
+        self.assertEqual(type(results), list)
121
+        self.assertEqual(len(results), 1)
122
+        self.assertEqual(results[0]['title'], 'This is the title')
123
+        self.assertEqual(results[0]['url'], 'https://thepiratebay.cr/this.is.the.link')
124
+        self.assertEqual(results[0]['content'], 'This is the content and should be OK')
125
+        self.assertEqual(results[0]['seed'], 0)
126
+        self.assertEqual(results[0]['leech'], 0)
127
+        self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETLINK')
128
+        self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/TORRENTFILE.torrent')
129
+
130
+        html = """
131
+        <table id="searchResult">
132
+        </table>
133
+        """
134
+        response = mock.Mock(text=html)
135
+        results = piratebay.response(response)
136
+        self.assertEqual(type(results), list)
137
+        self.assertEqual(len(results), 0)

+ 75
- 0
searx/tests/engines/test_searchcode_code.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import searchcode_code
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestSearchcodeCodeEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = searchcode_code.request(query, dicto)
14
+        self.assertIn('url', params)
15
+        self.assertIn(query, params['url'])
16
+        self.assertIn('searchcode.com', params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, searchcode_code.response, None)
20
+        self.assertRaises(AttributeError, searchcode_code.response, [])
21
+        self.assertRaises(AttributeError, searchcode_code.response, '')
22
+        self.assertRaises(AttributeError, searchcode_code.response, '[]')
23
+
24
+        response = mock.Mock(text='{}')
25
+        self.assertEqual(searchcode_code.response(response), [])
26
+
27
+        response = mock.Mock(text='{"data": []}')
28
+        self.assertEqual(searchcode_code.response(response), [])
29
+
30
+        json = """
31
+        {
32
+        "matchterm": "test",
33
+        "previouspage": null,
34
+        "searchterm": "test",
35
+        "query": "test",
36
+        "total": 1000,
37
+        "page": 0,
38
+        "nextpage": 1,
39
+        "results": [
40
+            {
41
+            "repo": "https://repo",
42
+            "linescount": 1044,
43
+            "location": "/tests",
44
+            "name": "Name",
45
+            "url": "https://url",
46
+            "md5hash": "ecac6e479edd2b9406c9e08603cec655",
47
+            "lines": {
48
+                "1": "// Test 011",
49
+                "2": "// Source: "
50
+            },
51
+            "id": 51223527,
52
+            "filename": "File.CPP"
53
+            }
54
+        ]
55
+        }
56
+        """
57
+        response = mock.Mock(text=json)
58
+        results = searchcode_code.response(response)
59
+        self.assertEqual(type(results), list)
60
+        self.assertEqual(len(results), 1)
61
+        self.assertEqual(results[0]['title'], 'Name - File.CPP')
62
+        self.assertEqual(results[0]['url'], 'https://url')
63
+        self.assertEqual(results[0]['repository'], 'https://repo')
64
+        self.assertEqual(results[0]['code_language'], 'cpp')
65
+
66
+        json = """
67
+        {"toto":[
68
+            {"id":200,"name":"Artist Name",
69
+            "link":"http:\/\/www.searchcode_code.com\/artist\/1217","type":"artist"}
70
+        ]}
71
+        """
72
+        response = mock.Mock(text=json)
73
+        results = searchcode_code.response(response)
74
+        self.assertEqual(type(results), list)
75
+        self.assertEqual(len(results), 0)

+ 73
- 0
searx/tests/engines/test_searchcode_doc.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import searchcode_doc
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestSearchcodeDocEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = searchcode_doc.request(query, dicto)
14
+        self.assertIn('url', params)
15
+        self.assertIn(query, params['url'])
16
+        self.assertIn('searchcode.com', params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, searchcode_doc.response, None)
20
+        self.assertRaises(AttributeError, searchcode_doc.response, [])
21
+        self.assertRaises(AttributeError, searchcode_doc.response, '')
22
+        self.assertRaises(AttributeError, searchcode_doc.response, '[]')
23
+
24
+        response = mock.Mock(text='{}')
25
+        self.assertEqual(searchcode_doc.response(response), [])
26
+
27
+        response = mock.Mock(text='{"data": []}')
28
+        self.assertEqual(searchcode_doc.response(response), [])
29
+
30
+        json = """
31
+        {
32
+        "matchterm": "test",
33
+        "previouspage": null,
34
+        "searchterm": "test",
35
+        "query": "test",
36
+        "total": 60,
37
+        "page": 0,
38
+        "nextpage": 1,
39
+        "results": [
40
+            {
41
+            "synopsis": "Synopsis",
42
+            "displayname": null,
43
+            "name": "test",
44
+            "url": "http://url",
45
+            "type": "Type",
46
+            "icon": null,
47
+            "namespace": "Namespace",
48
+            "description": "Description"
49
+            }
50
+        ]
51
+        }
52
+        """
53
+        response = mock.Mock(text=json)
54
+        results = searchcode_doc.response(response)
55
+        self.assertEqual(type(results), list)
56
+        self.assertEqual(len(results), 1)
57
+        self.assertEqual(results[0]['title'], '[Type] Namespace test')
58
+        self.assertEqual(results[0]['url'], 'http://url')
59
+        self.assertIn('Synopsis', results[0]['content'])
60
+        self.assertIn('Type', results[0]['content'])
61
+        self.assertIn('test', results[0]['content'])
62
+        self.assertIn('Description', results[0]['content'])
63
+
64
+        json = """
65
+        {"toto":[
66
+            {"id":200,"name":"Artist Name",
67
+            "link":"http:\/\/www.searchcode_doc.com\/artist\/1217","type":"artist"}
68
+        ]}
69
+        """
70
+        response = mock.Mock(text=json)
71
+        results = searchcode_doc.response(response)
72
+        self.assertEqual(type(results), list)
73
+        self.assertEqual(len(results), 0)

+ 192
- 0
searx/tests/engines/test_soundcloud.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import soundcloud
4
+from searx.testing import SearxTestCase
5
+from urllib import quote_plus
6
+
7
+
8
+class TestSoundcloudEngine(SearxTestCase):
9
+
10
+    def test_request(self):
11
+        query = 'test_query'
12
+        dicto = defaultdict(dict)
13
+        dicto['pageno'] = 1
14
+        params = soundcloud.request(query, dicto)
15
+        self.assertIn('url', params)
16
+        self.assertIn(query, params['url'])
17
+        self.assertIn('soundcloud.com', params['url'])
18
+
19
+    def test_response(self):
20
+        self.assertRaises(AttributeError, soundcloud.response, None)
21
+        self.assertRaises(AttributeError, soundcloud.response, [])
22
+        self.assertRaises(AttributeError, soundcloud.response, '')
23
+        self.assertRaises(AttributeError, soundcloud.response, '[]')
24
+
25
+        response = mock.Mock(text='{}')
26
+        self.assertEqual(soundcloud.response(response), [])
27
+
28
+        response = mock.Mock(text='{"data": []}')
29
+        self.assertEqual(soundcloud.response(response), [])
30
+
31
+        json = """
32
+        {
33
+        "collection": [
34
+            {
35
+            "kind": "track",
36
+            "id": 159723640,
37
+            "created_at": "2014/07/22 00:51:21 +0000",
38
+            "user_id": 2976616,
39
+            "duration": 303780,
40
+            "commentable": true,
41
+            "state": "finished",
42
+            "original_content_size": 13236349,
43
+            "last_modified": "2015/01/31 15:14:50 +0000",
44
+            "sharing": "public",
45
+            "tag_list": "seekae flume",
46
+            "permalink": "seekae-test-recognise-flume-re-work",
47
+            "streamable": true,
48
+            "embeddable_by": "all",
49
+            "downloadable": true,
50
+            "purchase_url": "http://www.facebook.com/seekaemusic",
51
+            "label_id": null,
52
+            "purchase_title": "Seekae",
53
+            "genre": "freedownload",
54
+            "title": "This is the title",
55
+            "description": "This is the content",
56
+            "label_name": "Future Classic",
57
+            "release": "",
58
+            "track_type": "remix",
59
+            "key_signature": "",
60
+            "isrc": "",
61
+            "video_url": null,
62
+            "bpm": null,
63
+            "release_year": 2014,
64
+            "release_month": 7,
65
+            "release_day": 22,
66
+            "original_format": "mp3",
67
+            "license": "all-rights-reserved",
68
+            "uri": "https://api.soundcloud.com/tracks/159723640",
69
+            "user": {
70
+                "id": 2976616,
71
+                "kind": "user",
72
+                "permalink": "flume",
73
+                "username": "Flume",
74
+                "last_modified": "2014/11/24 19:21:29 +0000",
75
+                "uri": "https://api.soundcloud.com/users/2976616",
76
+                "permalink_url": "http://soundcloud.com/flume",
77
+                "avatar_url": "https://i1.sndcdn.com/avatars-000044475439-4zi7ii-large.jpg"
78
+            },
79
+            "permalink_url": "http://soundcloud.com/this.is.the.url",
80
+            "artwork_url": "https://i1.sndcdn.com/artworks-000085857162-xdxy5c-large.jpg",
81
+            "waveform_url": "https://w1.sndcdn.com/DWrL1lAN8BkP_m.png",
82
+            "stream_url": "https://api.soundcloud.com/tracks/159723640/stream",
83
+            "download_url": "https://api.soundcloud.com/tracks/159723640/download",
84
+            "playback_count": 2190687,
85
+            "download_count": 54856,
86
+            "favoritings_count": 49061,
87
+            "comment_count": 826,
88
+            "likes_count": 49061,
89
+            "reposts_count": 15910,
90
+            "attachments_uri": "https://api.soundcloud.com/tracks/159723640/attachments",
91
+            "policy": "ALLOW"
92
+            }
93
+        ],
94
+        "total_results": 375750,
95
+        "next_href": "https://api.soundcloud.com/search?&q=test",
96
+        "tx_id": ""
97
+        }
98
+        """
99
+        response = mock.Mock(text=json)
100
+        results = soundcloud.response(response)
101
+        self.assertEqual(type(results), list)
102
+        self.assertEqual(len(results), 1)
103
+        self.assertEqual(results[0]['title'], 'This is the title')
104
+        self.assertEqual(results[0]['url'], 'http://soundcloud.com/this.is.the.url')
105
+        self.assertEqual(results[0]['content'], 'This is the content')
106
+        self.assertIn(quote_plus('https://api.soundcloud.com/tracks/159723640'), results[0]['embedded'])
107
+
108
+        json = """
109
+        {
110
+        "collection": [
111
+            {
112
+            "kind": "user",
113
+            "id": 159723640,
114
+            "created_at": "2014/07/22 00:51:21 +0000",
115
+            "user_id": 2976616,
116
+            "duration": 303780,
117
+            "commentable": true,
118
+            "state": "finished",
119
+            "original_content_size": 13236349,
120
+            "last_modified": "2015/01/31 15:14:50 +0000",
121
+            "sharing": "public",
122
+            "tag_list": "seekae flume",
123
+            "permalink": "seekae-test-recognise-flume-re-work",
124
+            "streamable": true,
125
+            "embeddable_by": "all",
126
+            "downloadable": true,
127
+            "purchase_url": "http://www.facebook.com/seekaemusic",
128
+            "label_id": null,
129
+            "purchase_title": "Seekae",
130
+            "genre": "freedownload",
131
+            "title": "This is the title",
132
+            "description": "This is the content",
133
+            "label_name": "Future Classic",
134
+            "release": "",
135
+            "track_type": "remix",
136
+            "key_signature": "",
137
+            "isrc": "",
138
+            "video_url": null,
139
+            "bpm": null,
140
+            "release_year": 2014,
141
+            "release_month": 7,
142
+            "release_day": 22,
143
+            "original_format": "mp3",
144
+            "license": "all-rights-reserved",
145
+            "uri": "https://api.soundcloud.com/tracks/159723640",
146
+            "user": {
147
+                "id": 2976616,
148
+                "kind": "user",
149
+                "permalink": "flume",
150
+                "username": "Flume",
151
+                "last_modified": "2014/11/24 19:21:29 +0000",
152
+                "uri": "https://api.soundcloud.com/users/2976616",
153
+                "permalink_url": "http://soundcloud.com/flume",
154
+                "avatar_url": "https://i1.sndcdn.com/avatars-000044475439-4zi7ii-large.jpg"
155
+            },
156
+            "permalink_url": "http://soundcloud.com/this.is.the.url",
157
+            "artwork_url": "https://i1.sndcdn.com/artworks-000085857162-xdxy5c-large.jpg",
158
+            "waveform_url": "https://w1.sndcdn.com/DWrL1lAN8BkP_m.png",
159
+            "stream_url": "https://api.soundcloud.com/tracks/159723640/stream",
160
+            "download_url": "https://api.soundcloud.com/tracks/159723640/download",
161
+            "playback_count": 2190687,
162
+            "download_count": 54856,
163
+            "favoritings_count": 49061,
164
+            "comment_count": 826,
165
+            "likes_count": 49061,
166
+            "reposts_count": 15910,
167
+            "attachments_uri": "https://api.soundcloud.com/tracks/159723640/attachments",
168
+            "policy": "ALLOW"
169
+            }
170
+        ],
171
+        "total_results": 375750,
172
+        "next_href": "https://api.soundcloud.com/search?&q=test",
173
+        "tx_id": ""
174
+        }
175
+        """
176
+        response = mock.Mock(text=json)
177
+        results = soundcloud.response(response)
178
+        self.assertEqual(type(results), list)
179
+        self.assertEqual(len(results), 0)
180
+
181
+        json = """
182
+        {
183
+        "collection": [],
184
+        "total_results": 375750,
185
+        "next_href": "https://api.soundcloud.com/search?&q=test",
186
+        "tx_id": ""
187
+        }
188
+        """
189
+        response = mock.Mock(text=json)
190
+        results = soundcloud.response(response)
191
+        self.assertEqual(type(results), list)
192
+        self.assertEqual(len(results), 0)

+ 106
- 0
searx/tests/engines/test_stackoverflow.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import stackoverflow
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestStackoverflowEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = stackoverflow.request(query, dicto)
14
+        self.assertTrue('url' in params)
15
+        self.assertTrue(query in params['url'])
16
+        self.assertTrue('stackoverflow.com' in params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, stackoverflow.response, None)
20
+        self.assertRaises(AttributeError, stackoverflow.response, [])
21
+        self.assertRaises(AttributeError, stackoverflow.response, '')
22
+        self.assertRaises(AttributeError, stackoverflow.response, '[]')
23
+
24
+        response = mock.Mock(text='<html></html>')
25
+        self.assertEqual(stackoverflow.response(response), [])
26
+
27
+        html = """
28
+        <div class="question-summary search-result" id="answer-id-1783426">
29
+            <div class="statscontainer">
30
+                <div class="statsarrow"></div>
31
+                <div class="stats">
32
+                    <div class="vote">
33
+                        <div class="votes answered">
34
+                            <span class="vote-count-post "><strong>2583</strong></span>
35
+                            <div class="viewcount">votes</div>
36
+                        </div>
37
+                    </div>
38
+                </div>
39
+            </div>
40
+            <div class="summary">
41
+                <div class="result-link">
42
+                    <span>
43
+                        <a href="/questions/this.is.the.url"
44
+                            data-searchsession="/questions"
45
+                            title="Checkout remote Git branch">
46
+                            This is the title
47
+                        </a>
48
+                    </span>
49
+                </div>
50
+                <div class="excerpt">
51
+                    This is the content
52
+                </div>
53
+                <div class="tags user-tags t-git t-git-checkout t-remote-branch">
54
+                </div>
55
+                <div class="started fr">
56
+                    answered <span title="2009-11-23 14:26:08Z" class="relativetime">nov 23 '09</span> by
57
+                    <a href="/users/214090/hallski">hallski</a>
58
+                </div>
59
+            </div>
60
+        </div>
61
+        """
62
+        response = mock.Mock(text=html)
63
+        results = stackoverflow.response(response)
64
+        self.assertEqual(type(results), list)
65
+        self.assertEqual(len(results), 1)
66
+        self.assertEqual(results[0]['title'], 'This is the title')
67
+        self.assertEqual(results[0]['url'], 'http://stackoverflow.com/questions/this.is.the.url')
68
+        self.assertEqual(results[0]['content'], 'This is the content')
69
+
70
+        html = """
71
+        <div class="statscontainer">
72
+            <div class="statsarrow"></div>
73
+            <div class="stats">
74
+                <div class="vote">
75
+                    <div class="votes answered">
76
+                        <span class="vote-count-post "><strong>2583</strong></span>
77
+                        <div class="viewcount">votes</div>
78
+                    </div>
79
+                </div>
80
+            </div>
81
+        </div>
82
+        <div class="summary">
83
+            <div class="result-link">
84
+                <span>
85
+                    <a href="/questions/this.is.the.url"
86
+                        data-searchsession="/questions"
87
+                        title="Checkout remote Git branch">
88
+                        This is the title
89
+                    </a>
90
+                </span>
91
+            </div>
92
+            <div class="excerpt">
93
+                This is the content
94
+            </div>
95
+            <div class="tags user-tags t-git t-git-checkout t-remote-branch">
96
+            </div>
97
+            <div class="started fr">
98
+                answered <span title="2009-11-23 14:26:08Z" class="relativetime">nov 23 '09</span> by
99
+                <a href="/users/214090/hallski">hallski</a>
100
+            </div>
101
+        </div>
102
+        """
103
+        response = mock.Mock(text=html)
104
+        results = stackoverflow.response(response)
105
+        self.assertEqual(type(results), list)
106
+        self.assertEqual(len(results), 0)

+ 84
- 0
searx/tests/engines/test_vimeo.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import vimeo
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestVimeoEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        params = vimeo.request(query, dicto)
14
+        self.assertTrue('url' in params)
15
+        self.assertTrue(query in params['url'])
16
+        self.assertTrue('vimeo.com' in params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, vimeo.response, None)
20
+        self.assertRaises(AttributeError, vimeo.response, [])
21
+        self.assertRaises(AttributeError, vimeo.response, '')
22
+        self.assertRaises(AttributeError, vimeo.response, '[]')
23
+
24
+        response = mock.Mock(text='<html></html>')
25
+        self.assertEqual(vimeo.response(response), [])
26
+
27
+        html = """
28
+        <div id="browse_content" class="" data-search-id="696d5f8366914ec4ffec33cf7652de384976d4f4">
29
+            <ol class="js-browse_list clearfix browse browse_videos browse_videos_thumbnails kane"
30
+                data-stream="c2VhcmNoOjo6ZGVzYzp7InF1ZXJ5IjoidGVzdCJ9">
31
+                <li id="clip_100785455" data-start-page="/search/page:1/sort:relevant/" data-position="1">
32
+                    <a href="/videoid" title="Futurama 3d (test shot)">
33
+                        <img src="http://image.url.webp"
34
+                            srcset="http://i.vimeocdn.com/video/482375085_590x332.webp 2x" alt=""
35
+                            class="thumbnail thumbnail_lg_wide">
36
+                        <div class="data">
37
+                            <p class="title">
38
+                                This is the title
39
+                            </p>
40
+                            <p class="meta">
41
+                                <time datetime="2014-07-15T04:16:27-04:00"
42
+                                    title="mardi 15 juillet 2014 04:16">Il y a 6 mois</time>
43
+                            </p>
44
+                        </div>
45
+                    </a>
46
+                </li>
47
+            </ol>
48
+        </div>
49
+        """
50
+        response = mock.Mock(text=html)
51
+        results = vimeo.response(response)
52
+        self.assertEqual(type(results), list)
53
+        self.assertEqual(len(results), 1)
54
+        self.assertEqual(results[0]['title'], 'This is the title')
55
+        self.assertEqual(results[0]['url'], 'http://vimeo.com/videoid')
56
+        self.assertEqual(results[0]['content'], '')
57
+        self.assertEqual(results[0]['thumbnail'], 'http://image.url.webp')
58
+        self.assertIn('/videoid', results[0]['embedded'])
59
+
60
+        html = """
61
+        <ol class="js-browse_list clearfix browse browse_videos browse_videos_thumbnails kane"
62
+            data-stream="c2VhcmNoOjo6ZGVzYzp7InF1ZXJ5IjoidGVzdCJ9">
63
+            <li id="clip_100785455" data-start-page="/search/page:1/sort:relevant/" data-position="1">
64
+                <a href="/videoid" title="Futurama 3d (test shot)">
65
+                    <img src="http://image.url.webp"
66
+                        srcset="http://i.vimeocdn.com/video/482375085_590x332.webp 2x" alt=""
67
+                        class="thumbnail thumbnail_lg_wide">
68
+                    <div class="data">
69
+                        <p class="title">
70
+                            This is the title
71
+                        </p>
72
+                        <p class="meta">
73
+                            <time datetime="2014-07-15T04:16:27-04:00"
74
+                                title="mardi 15 juillet 2014 04:16">Il y a 6 mois</time>
75
+                        </p>
76
+                    </div>
77
+                </a>
78
+            </li>
79
+        </ol>
80
+        """
81
+        response = mock.Mock(text=html)
82
+        results = vimeo.response(response)
83
+        self.assertEqual(type(results), list)
84
+        self.assertEqual(len(results), 0)

+ 83
- 0
searx/tests/engines/test_www500px.py View File

1
+# -*- coding: utf-8 -*-
2
+from collections import defaultdict
3
+import mock
4
+from searx.engines import www500px
5
+from searx.testing import SearxTestCase
6
+
7
+
8
+class TestWww500pxImagesEngine(SearxTestCase):
9
+
10
+    def test_request(self):
11
+        query = 'test_query'
12
+        dicto = defaultdict(dict)
13
+        dicto['pageno'] = 1
14
+        params = www500px.request(query, dicto)
15
+        self.assertTrue('url' in params)
16
+        self.assertTrue(query in params['url'])
17
+        self.assertTrue('500px.com' in params['url'])
18
+
19
+    def test_response(self):
20
+        self.assertRaises(AttributeError, www500px.response, None)
21
+        self.assertRaises(AttributeError, www500px.response, [])
22
+        self.assertRaises(AttributeError, www500px.response, '')
23
+        self.assertRaises(AttributeError, www500px.response, '[]')
24
+
25
+        response = mock.Mock(text='<html></html>')
26
+        self.assertEqual(www500px.response(response), [])
27
+
28
+        html = """
29
+        <div class="photo">
30
+            <a href="/this.should.be.the.url" data-ga-category="Photo Thumbnail" data-ga-action="Title">
31
+                <img src="https://image.url/3.jpg?v=0" />
32
+            </a>
33
+            <div class="details">
34
+                <div class="inside">
35
+                    <div class="title">
36
+                        <a href="/photo/64312705/branch-out-by-oliver-turpin?feature=">
37
+                            This is the title
38
+                        </a>
39
+                    </div>
40
+                    <div class="info">
41
+                        <a href="/ChronicleUK" data-ga-action="Image" data-ga-category="Photo Thumbnail">
42
+                            This is the content
43
+                        </a>
44
+                    </div>
45
+                    <div class="rating">44.8</div>
46
+                </div>
47
+            </div>
48
+        </div>
49
+        """
50
+        response = mock.Mock(text=html)
51
+        results = www500px.response(response)
52
+        self.assertEqual(type(results), list)
53
+        self.assertEqual(len(results), 1)
54
+        self.assertEqual(results[0]['title'], 'This is the title')
55
+        self.assertEqual(results[0]['url'], 'https://500px.com/this.should.be.the.url')
56
+        self.assertEqual(results[0]['content'], 'This is the content')
57
+        self.assertEqual(results[0]['thumbnail_src'], 'https://image.url/3.jpg?v=0')
58
+        self.assertEqual(results[0]['img_src'], 'https://image.url/2048.jpg')
59
+
60
+        html = """
61
+        <a href="/this.should.be.the.url" data-ga-category="Photo Thumbnail" data-ga-action="Title">
62
+            <img src="https://image.url/3.jpg?v=0" />
63
+        </a>
64
+        <div class="details">
65
+            <div class="inside">
66
+                <div class="title">
67
+                    <a href="/photo/64312705/branch-out-by-oliver-turpin?feature=">
68
+                        This is the title
69
+                    </a>
70
+                </div>
71
+                <div class="info">
72
+                    <a href="/ChronicleUK" data-ga-action="Image" data-ga-category="Photo Thumbnail">
73
+                        Oliver Turpin
74
+                    </a>
75
+                </div>
76
+                <div class="rating">44.8</div>
77
+            </div>
78
+        </div>
79
+        """
80
+        response = mock.Mock(text=html)
81
+        results = www500px.response(response)
82
+        self.assertEqual(type(results), list)
83
+        self.assertEqual(len(results), 0)

+ 204
- 0
searx/tests/engines/test_youtube.py View File

1
+from collections import defaultdict
2
+import mock
3
+from searx.engines import youtube
4
+from searx.testing import SearxTestCase
5
+
6
+
7
+class TestYoutubeEngine(SearxTestCase):
8
+
9
+    def test_request(self):
10
+        query = 'test_query'
11
+        dicto = defaultdict(dict)
12
+        dicto['pageno'] = 0
13
+        dicto['language'] = 'fr_FR'
14
+        params = youtube.request(query, dicto)
15
+        self.assertTrue('url' in params)
16
+        self.assertTrue(query in params['url'])
17
+        self.assertTrue('youtube.com' in params['url'])
18
+        self.assertTrue('fr' in params['url'])
19
+
20
+        dicto['language'] = 'all'
21
+        params = youtube.request(query, dicto)
22
+        self.assertFalse('fr' in params['url'])
23
+
24
+    def test_response(self):
25
+        self.assertRaises(AttributeError, youtube.response, None)
26
+        self.assertRaises(AttributeError, youtube.response, [])
27
+        self.assertRaises(AttributeError, youtube.response, '')
28
+        self.assertRaises(AttributeError, youtube.response, '[]')
29
+
30
+        response = mock.Mock(text='{}')
31
+        self.assertEqual(youtube.response(response), [])
32
+
33
+        response = mock.Mock(text='{"data": []}')
34
+        self.assertEqual(youtube.response(response), [])
35
+
36
+        json = """
37
+        {"feed":{"entry":[{
38
+            "id":{"$t":"http://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM"},
39
+            "published":{"$t":"2015-01-23T21:25:00.000Z"},
40
+            "updated":{"$t":"2015-01-26T14:38:15.000Z"},
41
+            "title":{"$t":"Title",
42
+                "type":"text"},"content":{"$t":"Description","type":"text"},
43
+            "link":[{"rel":"alternate","type":"text/html",
44
+                "href":"https://www.youtube.com/watch?v=DIVZCPfAOeM&feature=youtube_gdata"},
45
+                {"rel":"http://gdata.youtube.com/schemas/2007#video.related",
46
+                "type":"application/atom+xml",
47
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM/related"},
48
+                {"rel":"http://gdata.youtube.com/schemas/2007#mobile","type":"text/html",
49
+                "href":"https://m.youtube.com/details?v=DIVZCPfAOeM"},
50
+                {"rel":"self","type":"application/atom+xml",
51
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM"}],
52
+            "author":[{"name":{"$t":"Cauet"},
53
+                "uri":{"$t":"https://gdata.youtube.com/feeds/api/users/cauetofficiel"} }],
54
+            "gd$comments":{"gd$feedLink":{"rel":"http://gdata.youtube.com/schemas/2007#comments",
55
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM/comments",
56
+                "countHint":8} },
57
+            "media$group":{"media$category":[{"$t":"Comedy","label":"Comedy",
58
+                "scheme":"http://gdata.youtube.com/schemas/2007/categories.cat"}],
59
+            "media$content":[{"url":"https://www.youtube.com/v/DIVZCPfAOeM?version=3&f=videos&app=youtube_gdata",
60
+                "type":"application/x-shockwave-flash","medium":"video",
61
+                "isDefault":"true","expression":"full","duration":354,"yt$format":5},
62
+    {"url":"rtsp://r1---sn-cg07luel.c.youtube.com/CiILENy73wIaGQnjOcD3CFmFDBMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
63
+                "type":"video/3gpp","medium":"video","expression":"full","duration":354,
64
+                "yt$format":1},
65
+    {"url":"rtsp://r1---sn-cg07luel.c.youtube.com/CiILENy73wIaGQnjOcD3CFmFDBMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
66
+                "type":"video/3gpp","medium":"video","expression":"full","duration":354,"yt$format":6}],
67
+            "media$description":{"$t":"Desc","type":"plain"},
68
+            "media$keywords":{},
69
+            "media$player":[{"url":"https://www.youtube.com/watch?v=DIVZCPfAOeM&feature=youtube_gdata_player"}],
70
+            "media$thumbnail":[{"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/0.jpg",
71
+                    "height":360,"width":480,"time":"00:02:57"},
72
+                {"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/1.jpg","height":90,"width":120,"time":"00:01:28.500"},
73
+                {"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/2.jpg","height":90,"width":120,"time":"00:02:57"},
74
+                {"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/3.jpg","height":90,"width":120,"time":"00:04:25.500"}],
75
+            "media$title":{"$t":"Title","type":"plain"},
76
+            "yt$duration":{"seconds":"354"} },
77
+            "gd$rating":{"average":4.932159,"max":5,"min":1,"numRaters":1533,
78
+                "rel":"http://schemas.google.com/g/2005#overall"},
79
+            "yt$statistics":{"favoriteCount":"0","viewCount":"92464"} }
80
+            ]
81
+        }
82
+        }
83
+        """
84
+        response = mock.Mock(text=json)
85
+        results = youtube.response(response)
86
+        self.assertEqual(type(results), list)
87
+        self.assertEqual(len(results), 1)
88
+        self.assertEqual(results[0]['title'], 'Title')
89
+        self.assertEqual(results[0]['url'], 'https://www.youtube.com/watch?v=DIVZCPfAOeM')
90
+        self.assertEqual(results[0]['content'], 'Description')
91
+        self.assertEqual(results[0]['thumbnail'], 'https://i.ytimg.com/vi/DIVZCPfAOeM/0.jpg')
92
+        self.assertTrue('DIVZCPfAOeM' in results[0]['embedded'])
93
+
94
+        json = """
95
+        {"feed":{"entry":[{
96
+            "id":{"$t":"http://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM"},
97
+            "published":{"$t":"2015-01-23T21:25:00.000Z"},
98
+            "updated":{"$t":"2015-01-26T14:38:15.000Z"},
99
+            "title":{"$t":"Title",
100
+                "type":"text"},"content":{"$t":"Description","type":"text"},
101
+            "link":[{"rel":"http://gdata.youtube.com/schemas/2007#video.related",
102
+                "type":"application/atom+xml",
103
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM/related"},
104
+                {"rel":"self","type":"application/atom+xml",
105
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM"}],
106
+            "author":[{"name":{"$t":"Cauet"},
107
+                "uri":{"$t":"https://gdata.youtube.com/feeds/api/users/cauetofficiel"} }],
108
+            "gd$comments":{"gd$feedLink":{"rel":"http://gdata.youtube.com/schemas/2007#comments",
109
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM/comments",
110
+                "countHint":8} },
111
+            "media$group":{"media$category":[{"$t":"Comedy","label":"Comedy",
112
+                "scheme":"http://gdata.youtube.com/schemas/2007/categories.cat"}],
113
+            "media$content":[{"url":"https://www.youtube.com/v/DIVZCPfAOeM?version=3&f=videos&app=youtube_gdata",
114
+                "type":"application/x-shockwave-flash","medium":"video",
115
+                "isDefault":"true","expression":"full","duration":354,"yt$format":5},
116
+    {"url":"rtsp://r1---sn-cg07luel.c.youtube.com/CiILENy73wIaGQnjOcD3CFmFDBMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
117
+                "type":"video/3gpp","medium":"video","expression":"full","duration":354,
118
+                "yt$format":1},
119
+    {"url":"rtsp://r1---sn-cg07luel.c.youtube.com/CiILENy73wIaGQnjOcD3CFmFDBMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
120
+                "type":"video/3gpp","medium":"video","expression":"full","duration":354,"yt$format":6}],
121
+            "media$description":{"$t":"Desc","type":"plain"},
122
+            "media$keywords":{},
123
+            "media$player":[{"url":"https://www.youtube.com/watch?v=DIVZCPfAOeM&feature=youtube_gdata_player"}],
124
+            "media$thumbnail":[{"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/0.jpg",
125
+                    "height":360,"width":480,"time":"00:02:57"},
126
+                {"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/1.jpg","height":90,"width":120,"time":"00:01:28.500"},
127
+                {"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/2.jpg","height":90,"width":120,"time":"00:02:57"},
128
+                {"url":"https://i.ytimg.com/vi/DIVZCPfAOeM/3.jpg","height":90,"width":120,"time":"00:04:25.500"}],
129
+            "media$title":{"$t":"Title","type":"plain"},
130
+            "yt$duration":{"seconds":"354"} },
131
+            "gd$rating":{"average":4.932159,"max":5,"min":1,"numRaters":1533,
132
+                "rel":"http://schemas.google.com/g/2005#overall"},
133
+            "yt$statistics":{"favoriteCount":"0","viewCount":"92464"} }
134
+            ]
135
+        }
136
+        }
137
+        """
138
+        response = mock.Mock(text=json)
139
+        results = youtube.response(response)
140
+        self.assertEqual(type(results), list)
141
+        self.assertEqual(len(results), 0)
142
+
143
+        json = """
144
+        {"feed":{"entry":[{
145
+            "id":{"$t":"http://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM"},
146
+            "published":{"$t":"2015-01-23T21:25:00.000Z"},
147
+            "updated":{"$t":"2015-01-26T14:38:15.000Z"},
148
+            "title":{"$t":"Title",
149
+                "type":"text"},"content":{"$t":"Description","type":"text"},
150
+            "link":[{"rel":"alternate","type":"text/html",
151
+                "href":"https://www.youtube.com/watch?v=DIVZCPfAOeM"},
152
+                {"rel":"http://gdata.youtube.com/schemas/2007#video.related",
153
+                "type":"application/atom+xml",
154
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM/related"},
155
+                {"rel":"http://gdata.youtube.com/schemas/2007#mobile","type":"text/html",
156
+                "href":"https://m.youtube.com/details?v=DIVZCPfAOeM"},
157
+                {"rel":"self","type":"application/atom+xml",
158
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM"}],
159
+            "author":[{"name":{"$t":"Cauet"},
160
+                "uri":{"$t":"https://gdata.youtube.com/feeds/api/users/cauetofficiel"} }],
161
+            "gd$comments":{"gd$feedLink":{"rel":"http://gdata.youtube.com/schemas/2007#comments",
162
+                "href":"https://gdata.youtube.com/feeds/api/videos/DIVZCPfAOeM/comments",
163
+                "countHint":8} },
164
+            "media$group":{"media$category":[{"$t":"Comedy","label":"Comedy",
165
+                "scheme":"http://gdata.youtube.com/schemas/2007/categories.cat"}],
166
+            "media$content":[{"url":"https://www.youtube.com/v/DIVZCPfAOeM?version=3&f=videos&app=youtube_gdata",
167
+                "type":"application/x-shockwave-flash","medium":"video",
168
+                "isDefault":"true","expression":"full","duration":354,"yt$format":5},
169
+    {"url":"rtsp://r1---sn-cg07luel.c.youtube.com/CiILENy73wIaGQnjOcD3CFmFDBMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
170
+                "type":"video/3gpp","medium":"video","expression":"full","duration":354,
171
+                "yt$format":1},
172
+    {"url":"rtsp://r1---sn-cg07luel.c.youtube.com/CiILENy73wIaGQnjOcD3CFmFDBMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
173
+                "type":"video/3gpp","medium":"video","expression":"full","duration":354,"yt$format":6}],
174
+            "media$description":{"$t":"Desc","type":"plain"},
175
+            "media$keywords":{},
176
+            "media$player":[{"url":"https://www.youtube.com/watch?v=DIVZCPfAOeM&feature=youtube_gdata_player"}],
177
+            "media$title":{"$t":"Title","type":"plain"},
178
+            "yt$duration":{"seconds":"354"} },
179
+            "gd$rating":{"average":4.932159,"max":5,"min":1,"numRaters":1533,
180
+                "rel":"http://schemas.google.com/g/2005#overall"},
181
+            "yt$statistics":{"favoriteCount":"0","viewCount":"92464"} }
182
+            ]
183
+        }
184
+        }
185
+        """
186
+        response = mock.Mock(text=json)
187
+        results = youtube.response(response)
188
+        self.assertEqual(type(results), list)
189
+        self.assertEqual(len(results), 1)
190
+        self.assertEqual(results[0]['title'], 'Title')
191
+        self.assertEqual(results[0]['url'], 'https://www.youtube.com/watch?v=DIVZCPfAOeM')
192
+        self.assertEqual(results[0]['content'], 'Description')
193
+        self.assertEqual(results[0]['thumbnail'], '')
194
+        self.assertTrue('DIVZCPfAOeM' in results[0]['embedded'])
195
+
196
+        json = """
197
+        {"toto":{"entry":[]
198
+        }
199
+        }
200
+        """
201
+        response = mock.Mock(text=json)
202
+        results = youtube.response(response)
203
+        self.assertEqual(type(results), list)
204
+        self.assertEqual(len(results), 0)

+ 22
- 0
searx/tests/test_engines.py View File

1
+from searx.tests.engines.test_bing import *  # noqa
2
+from searx.tests.engines.test_bing_images import *  # noqa
3
+from searx.tests.engines.test_bing_news import *  # noqa
4
+from searx.tests.engines.test_btdigg import *  # noqa
5
+from searx.tests.engines.test_dailymotion import *  # noqa
6
+from searx.tests.engines.test_deezer import *  # noqa
7
+from searx.tests.engines.test_deviantart import *  # noqa
8
+from searx.tests.engines.test_digg import *  # noqa
1
 from searx.tests.engines.test_dummy import *  # noqa
9
 from searx.tests.engines.test_dummy import *  # noqa
10
+from searx.tests.engines.test_flickr import *  # noqa
11
+from searx.tests.engines.test_flickr_noapi import *  # noqa
2
 from searx.tests.engines.test_github import *  # noqa
12
 from searx.tests.engines.test_github import *  # noqa
3
 from searx.tests.engines.test_www1x import *  # noqa
13
 from searx.tests.engines.test_www1x import *  # noqa
14
+from searx.tests.engines.test_google_images import *  # noqa
15
+from searx.tests.engines.test_google_news import *  # noqa
16
+from searx.tests.engines.test_kickass import *  # noqa
17
+from searx.tests.engines.test_mixcloud import *  # noqa
18
+from searx.tests.engines.test_piratebay import *  # noqa
19
+from searx.tests.engines.test_searchcode_code import *  # noqa
20
+from searx.tests.engines.test_searchcode_doc import *  # noqa
21
+from searx.tests.engines.test_soundcloud import *  # noqa
22
+from searx.tests.engines.test_stackoverflow import *  # noqa
23
+from searx.tests.engines.test_vimeo import *  # noqa
24
+from searx.tests.engines.test_www500px import *  # noqa
25
+from searx.tests.engines.test_youtube import *  # noqa

+ 22
- 0
searx/tests/test_utils.py View File

10
         self.assertIsNotNone(utils.gen_useragent())
10
         self.assertIsNotNone(utils.gen_useragent())
11
         self.assertTrue(utils.gen_useragent().startswith('Mozilla'))
11
         self.assertTrue(utils.gen_useragent().startswith('Mozilla'))
12
 
12
 
13
+    def test_searx_useragent(self):
14
+        self.assertIsInstance(utils.searx_useragent(), str)
15
+        self.assertIsNotNone(utils.searx_useragent())
16
+        self.assertTrue(utils.searx_useragent().startswith('searx'))
17
+
13
     def test_highlight_content(self):
18
     def test_highlight_content(self):
14
         self.assertEqual(utils.highlight_content(0, None), None)
19
         self.assertEqual(utils.highlight_content(0, None), None)
15
         self.assertEqual(utils.highlight_content(None, None), None)
20
         self.assertEqual(utils.highlight_content(None, None), None)
29
         query = 'a test'
34
         query = 'a test'
30
         self.assertEqual(utils.highlight_content(content, query), content)
35
         self.assertEqual(utils.highlight_content(content, query), content)
31
 
36
 
37
+    def test_html_to_text(self):
38
+        html = """
39
+        <a href="/testlink" class="link_access_account">
40
+            <span class="toto">
41
+                <span>
42
+                    <img src="test.jpg" />
43
+                </span>
44
+            </span>
45
+            <span class="titi">
46
+                            Test text
47
+            </span>
48
+        </a>
49
+        """
50
+        self.assertIsInstance(utils.html_to_text(html), unicode)
51
+        self.assertIsNotNone(utils.html_to_text(html))
52
+        self.assertEqual(utils.html_to_text(html), "Test text")
53
+
32
 
54
 
33
 class TestHTMLTextExtractor(SearxTestCase):
55
 class TestHTMLTextExtractor(SearxTestCase):
34
 
56
 

+ 3
- 1
searx/utils.py View File

115
         self.result.append(name)
115
         self.result.append(name)
116
 
116
 
117
     def get_text(self):
117
     def get_text(self):
118
-        return u''.join(self.result)
118
+        return u''.join(self.result).strip()
119
 
119
 
120
 
120
 
121
 def html_to_text(html):
121
 def html_to_text(html):
122
+    html = html.replace('\n', ' ')
123
+    html = ' '.join(html.split())
122
     s = HTMLTextExtractor()
124
     s = HTMLTextExtractor()
123
     s.feed(html)
125
     s.feed(html)
124
     return s.get_text()
126
     return s.get_text()