on master help sign in
Merge branch 'Cqoicebordel-unit-tests'
commit0e6f8393ab8b29b2e85d1fafdc7442455767f753 authorAdam Tauber <adam.tauber@balabit.com> dateMon 2 Feb 2015, 09:37 (12 years ago) parent03137ee7f86535
45 files changed+3692−71
.gitignore+1 searx/engines/bing.py+5−4 searx/engines/bing_images.py+4−1 searx/engines/bing_news.py+12−19 searx/engines/btdigg.py+3−8 searx/engines/deviantart.py+4−3 searx/engines/digg.py+1−1 searx/engines/flickr_noapi.py+1−1 searx/engines/google_images.py+3−3 searx/engines/google_news.py+4−2 searx/engines/kickass.py+4−4 searx/engines/piratebay.py+8−4 searx/engines/searchcode_code.py+1−1 searx/engines/searchcode_doc.py+1−1 searx/engines/stackoverflow.py+4−4 searx/engines/vimeo.py+1−2 searx/engines/www500px.py+6−5 searx/engines/xpath.py+2−2 searx/engines/youtube.py+2−2 searx/settings.yml+3−3 searx/tests/engines/test_bing.py+90 searx/tests/engines/test_bing_images.py+268 searx/tests/engines/test_bing_news.py+236 searx/tests/engines/test_btdigg.py+384 searx/tests/engines/test_dailymotion.py+74 searx/tests/engines/test_deezer.py+57 searx/tests/engines/test_deviantart.py+118 searx/tests/engines/test_digg.py+101 searx/tests/engines/test_flickr.py+142 searx/tests/engines/test_flickr_noapi.py+442 searx/tests/engines/test_google_images.py+108 searx/tests/engines/test_google_news.py+136 searx/tests/engines/test_kickass.py+398 searx/tests/engines/test_mixcloud.py+67 searx/tests/engines/test_piratebay.py+137 searx/tests/engines/test_searchcode_code.py+75 searx/tests/engines/test_searchcode_doc.py+73 searx/tests/engines/test_soundcloud.py+192 searx/tests/engines/test_stackoverflow.py+106 searx/tests/engines/test_vimeo.py+84 searx/tests/engines/test_www500px.py+83 searx/tests/engines/test_youtube.py+204 searx/tests/test_engines.py+22 searx/tests/test_utils.py+22 searx/utils.py+3−1
.gitignore+1
@@ -23,3 +23,4 @@ local/
2323parts/
2424searx.egg-info/
2525var/
26+node_modules/
searx/engines/bing.py+5−4
@@ -14,6 +14,7 @@
1414from urllib import urlencode
1515from cgi import escape
1616from lxml import html
17+from searx.engines.xpath import extract_text
1718
1819# engine dependent config
1920categories = ['general']
@@ -55,8 +56,8 @@ def response(resp):
5556 for result in dom.xpath('//div[@class="sa_cc"]'):
5657 link = result.xpath('.//h3/a')[0]
5758 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')))
6061
6162 # append result
6263 results.append({'url': url,
@@ -71,8 +72,8 @@ def response(resp):
7172 for result in dom.xpath('//li[@class="b_algo"]'):
7273 link = result.xpath('.//h2/a')[0]
7374 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')))
7677
7778 # append result
7879 results.append({'url': url,
searx/engines/bing_images.py+4−1
@@ -33,7 +33,10 @@ def request(query, params):
3333 offset = (params['pageno'] - 1) * 10 + 1
3434
3535 # required for cookie
36− language = 'en-US'
36+ if params['language'] == 'all':
37+ language = 'en-US'
38+ else:
39+ language = params['language'].replace('_', '-')
3740
3841 search_path = search_string.format(
3942 query=urlencode({'q': query}),
searx/engines/bing_news.py+12−19
@@ -15,6 +15,7 @@ from lxml import html
1515from datetime import datetime, timedelta
1616from dateutil import parser
1717import re
18+from searx.engines.xpath import extract_text
1819
1920# engine dependent config
2021categories = ['news']
@@ -42,6 +43,7 @@ def request(query, params):
4243 params['cookies']['_FP'] = "ui=en-US"
4344
4445 params['url'] = base_url + search_path
46+
4547 return params
4648
4749
@@ -55,44 +57,35 @@ def response(resp):
5557 for result in dom.xpath('//div[@class="sn_r"]'):
5658 link = result.xpath('.//div[@class="newstitle"]/a')[0]
5759 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))
6363
6464 # parse publishedDate
6565 publishedDateXPath = result.xpath('.//div[@class="sn_txt"]/div'
6666 '//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))
7170
7271 if re.match("^[0-9]+ minute(s|) ago$", publishedDate):
7372 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]))
7674 elif re.match("^[0-9]+ hour(s|) ago$", publishedDate):
7775 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):
8278 timeNumbers = re.findall(r'\d+', publishedDate)
8379 publishedDate = datetime.now()\
8480 - timedelta(hours=int(timeNumbers[0]))\
8581 - timedelta(minutes=int(timeNumbers[1]))
8682 elif re.match("^[0-9]+ day(s|) ago$", publishedDate):
8783 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]))
9085 else:
9186 try:
92− # FIXME use params['language'] to parse either mm/dd or dd/mm
9387 publishedDate = parser.parse(publishedDate, dayfirst=False)
9488 except TypeError:
95− # FIXME
9689 publishedDate = datetime.now()
9790
9891 # append result
searx/engines/btdigg.py+3−8
@@ -23,11 +23,6 @@ paging = True
2323url = 'https://btdigg.org'
2424search_url = url + '/search?q={search_term}&p={pageno}'
2525
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−
3126
3227# do search-request
3328def request(query, params):
@@ -52,8 +47,8 @@ def response(resp):
5247 # parse results
5348 for result in search_res:
5449 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))
5752 content = escape(extract_text(result.xpath('.//pre[@class="snippet"]')[0]))
5853 content = "<br />".join(content.split("\n"))
5954
@@ -81,7 +76,7 @@ def response(resp):
8176 filesize = int(filesize * 1024 * 1024 * 1024)
8277 elif filesize_multiplier == 'MB':
8378 filesize = int(filesize * 1024 * 1024)
84− elif filesize_multiplier == 'kb':
79+ elif filesize_multiplier == 'KB':
8580 filesize = int(filesize * 1024)
8681 except:
8782 filesize = None
searx/engines/deviantart.py+4−3
@@ -14,6 +14,7 @@ from urllib import urlencode
1414from urlparse import urljoin
1515from lxml import html
1616import re
17+from searx.engines.xpath import extract_text
1718
1819# engine dependent config
1920categories = ['images']
@@ -50,9 +51,9 @@ def response(resp):
5051 for result in dom.xpath('//div[contains(@class, "tt-a tt-fh")]'):
5152 link = result.xpath('.//a[contains(@class, "thumb")]')[0]
5253 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')
5657 img_src = regex.sub('/', thumbnail_src)
5758
5859 # append result
searx/engines/digg.py+1−1
@@ -44,7 +44,7 @@ def response(resp):
4444
4545 search_result = loads(resp.text)
4646
47− if search_result['html'] == '':
47+ if 'html' not in search_result or search_result['html'] == '':
4848 return results
4949
5050 dom = html.fromstring(search_result['html'])
searx/engines/flickr_noapi.py+1−1
@@ -21,7 +21,7 @@ logger = logger.getChild('flickr-noapi')
2121categories = ['images']
2222
2323url = 'https://secure.flickr.com/'
24−search_url = url+'search/?{query}&page={page}'
24+search_url = url + 'search/?{query}&page={page}'
2525photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
2626regex = re.compile(r"\"search-photos-models\",\"photos\":(.*}),\"totalItems\":", re.DOTALL)
2727image_sizes = ('o', 'k', 'h', 'b', 'c', 'z', 'n', 'm', 't', 'q', 's')
searx/engines/google_images.py+3−3
@@ -18,7 +18,7 @@ paging = True
1818
1919# search-url
2020url = '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}'
2222
2323
2424# do search-request
@@ -45,14 +45,14 @@ def response(resp):
4545 for result in search_res['responseData']['results']:
4646 href = result['originalContextUrl']
4747 title = result['title']
48− if not result['url']:
48+ if 'url' not in result:
4949 continue
5050 thumbnail_src = result['tbUrl']
5151
5252 # append result
5353 results.append({'url': href,
5454 'title': title,
55− 'content': '',
55+ 'content': result['content'],
5656 'thumbnail_src': thumbnail_src,
5757 'img_src': unquote(result['url']),
5858 'template': 'images.html'})
searx/engines/google_news.py+4−2
@@ -20,7 +20,7 @@ language_support = True
2020
2121# engine dependent config
2222url = '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}'
2424
2525
2626# do search-request
@@ -33,7 +33,7 @@ def request(query, params):
3333
3434 params['url'] = search_url.format(offset=offset,
3535 query=urlencode({'q': query}),
36− language=language)
36+ lang=language)
3737
3838 return params
3939
@@ -52,6 +52,8 @@ def response(resp):
5252 for result in search_res['responseData']['results']:
5353 # parse publishedDate
5454 publishedDate = parser.parse(result['publishedDate'])
55+ if 'url' not in result:
56+ continue
5557
5658 # append result
5759 results.append({'url': result['unescapedUrl'],
searx/engines/kickass.py+4−4
@@ -13,6 +13,7 @@ from cgi import escape
1313from urllib import quote
1414from lxml import html
1515from operator import itemgetter
16+from searx.engines.xpath import extract_text
1617
1718# engine dependent config
1819categories = ['videos', 'music', 'files']
@@ -56,9 +57,8 @@ def response(resp):
5657 for result in search_res[1:]:
5758 link = result.xpath('.//a[@class="cellMainLink"]')[0]
5859 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)))
6262 seed = result.xpath('.//td[contains(@class, "green")]/text()')[0]
6363 leech = result.xpath('.//td[contains(@class, "red")]/text()')[0]
6464 filesize = result.xpath('.//td[contains(@class, "nobr")]/text()')[0]
@@ -88,7 +88,7 @@ def response(resp):
8888 filesize = int(filesize * 1024 * 1024 * 1024)
8989 elif filesize_multiplier == 'MB':
9090 filesize = int(filesize * 1024 * 1024)
91− elif filesize_multiplier == 'kb':
91+ elif filesize_multiplier == 'KB':
9292 filesize = int(filesize * 1024)
9393 except:
9494 filesize = None
searx/engines/piratebay.py+8−4
@@ -13,6 +13,7 @@ from cgi import escape
1313from urllib import quote
1414from lxml import html
1515from operator import itemgetter
16+from searx.engines.xpath import extract_text
1617
1718# engine dependent config
1819categories = ['videos', 'music', 'files']
@@ -29,7 +30,8 @@ search_types = {'files': '0',
2930
3031# specific xpath variables
3132magnet_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"]'
3335
3436
3537# do search-request
@@ -59,8 +61,8 @@ def response(resp):
5961 for result in search_res[1:]:
6062 link = result.xpath('.//div[@class="detName"]//a')[0]
6163 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)))
6466 seed, leech = result.xpath('.//td[@align="right"]/text()')[:2]
6567
6668 # convert seed to int if possible
@@ -76,6 +78,7 @@ def response(resp):
7678 leech = 0
7779
7880 magnetlink = result.xpath(magnet_xpath)[0]
81+ torrentfile = result.xpath(torrent_xpath)[0]
7982
8083 # append result
8184 results.append({'url': href,
@@ -83,7 +86,8 @@ def response(resp):
8386 'content': content,
8487 'seed': seed,
8588 'leech': leech,
86− 'magnetlink': magnetlink.attrib['href'],
89+ 'magnetlink': magnetlink.attrib.get('href'),
90+ 'torrentfile': torrentfile.attrib.get('href'),
8791 'template': 'torrent.html'})
8892
8993 # return results sorted by seeder
searx/engines/searchcode_code.py+1−1
@@ -42,7 +42,7 @@ def response(resp):
4242 search_results = loads(resp.text)
4343
4444 # parse results
45− for result in search_results['results']:
45+ for result in search_results.get('results', []):
4646 href = result['url']
4747 title = "" + result['name'] + " - " + result['filename']
4848 repo = result['repo']
searx/engines/searchcode_doc.py+1−1
@@ -35,7 +35,7 @@ def response(resp):
3535 search_results = loads(resp.text)
3636
3737 # parse results
38− for result in search_results['results']:
38+ for result in search_results.get('results', []):
3939 href = result['url']
4040 title = "[" + result['type'] + "] " +\
4141 result['namespace'] +\
searx/engines/stackoverflow.py+4−4
@@ -12,6 +12,7 @@ from urlparse import urljoin
1212from cgi import escape
1313from urllib import urlencode
1414from lxml import html
15+from searx.engines.xpath import extract_text
1516
1617# engine dependent config
1718categories = ['it']
@@ -24,8 +25,7 @@ search_url = url+'search?{query}&page={pageno}'
2425# specific xpath variables
2526results_xpath = '//div[contains(@class,"question-summary")]'
2627link_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"]'
2929
3030
3131# do search-request
@@ -46,8 +46,8 @@ def response(resp):
4646 for result in dom.xpath(results_xpath):
4747 link = result.xpath(link_xpath)[0]
4848 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)))
5151
5252 # append result
5353 results.append({'url': href,
searx/engines/vimeo.py+1−2
@@ -59,8 +59,7 @@ def response(resp):
5959 url = base_url + videoid
6060 title = p.unescape(extract_text(result.xpath(title_xpath)))
6161 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]))
6463 embedded = embedded_url.format(videoid=videoid)
6564
6665 # append result
searx/engines/www500px.py+6−5
@@ -15,6 +15,7 @@ from urllib import urlencode
1515from urlparse import urljoin
1616from lxml import html
1717import re
18+from searx.engines.xpath import extract_text
1819
1920# engine dependent config
2021categories = ['images']
@@ -22,7 +23,7 @@ paging = True
2223
2324# search-url
2425base_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}'
2627
2728
2829# do search-request
@@ -44,11 +45,11 @@ def response(resp):
4445 for result in dom.xpath('//div[@class="photo"]'):
4546 link = result.xpath('.//a')[0]
4647 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')
4950 # 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"]'))
5253 img_src = regex.sub('2048.jpg', thumbnail_src)
5354
5455 # append result
searx/engines/xpath.py+2−2
@@ -28,13 +28,13 @@ def extract_text(xpath_results):
2828 result = ''
2929 for e in xpath_results:
3030 result = result + extract_text(e)
31− return result
31+ return result.strip()
3232 elif type(xpath_results) in [_ElementStringResult, _ElementUnicodeResult]:
3333 # it's a string
3434 return ''.join(xpath_results)
3535 else:
3636 # it's a element
37− return html_to_text(xpath_results.text_content())
37+ return html_to_text(xpath_results.text_content()).strip()
3838
3939
4040def extract_url(xpath_results, search_url):
searx/engines/youtube.py+2−2
@@ -57,7 +57,7 @@ def response(resp):
5757 url = [x['href'] for x in result['link'] if x['type'] == 'text/html']
5858
5959 if not url:
60− return
60+ continue
6161
6262 # remove tracking
6363 url = url[0].replace('feature=youtube_gdata', '')
@@ -73,7 +73,7 @@ def response(resp):
7373 pubdate = result['published']['$t']
7474 publishedDate = parser.parse(pubdate)
7575
76− if result['media$group']['media$thumbnail']:
76+ if 'media$thumbnail' in result['media$group']:
7777 thumbnail = result['media$group']['media$thumbnail'][0]['url']
7878
7979 content = result['content']['$t']
searx/settings.yml+3−3
@@ -161,9 +161,9 @@ engines:
161161 engine : photon
162162 shortcut : ph
163163
164−# - name : piratebay
165−# engine : piratebay
166−# shortcut : tpb
164+ - name : piratebay
165+ engine : piratebay
166+ shortcut : tpb
167167
168168 - name : kickass
169169 engine : kickass
searx/tests/engines/test_bing.pynew file+90
@@ -0,0 +1,90 @@
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.')
searx/tests/engines/test_bing_images.pynew file+268
@@ -0,0 +1,268 @@
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)
searx/tests/engines/test_bing_news.pynew file+236
@@ -0,0 +1,236 @@
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)
searx/tests/engines/test_btdigg.pynew file+384
@@ -0,0 +1,384 @@
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)
searx/tests/engines/test_dailymotion.pynew file+74
@@ -0,0 +1,74 @@
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)
searx/tests/engines/test_deezer.pynew file+57
@@ -0,0 +1,57 @@
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)
searx/tests/engines/test_deviantart.pynew file+118
@@ -0,0 +1,118 @@
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)
searx/tests/engines/test_digg.pynew file+101
@@ -0,0 +1,101 @@
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)
searx/tests/engines/test_flickr.pynew file+142
@@ -0,0 +1,142 @@
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)
searx/tests/engines/test_flickr_noapi.pynew file+442
@@ -0,0 +1,442 @@
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)
searx/tests/engines/test_google_images.pynew file+108
@@ -0,0 +1,108 @@
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)
searx/tests/engines/test_google_news.pynew file+136
@@ -0,0 +1,136 @@
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)
searx/tests/engines/test_kickass.pynew file+398
@@ -0,0 +1,398 @@
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)
searx/tests/engines/test_mixcloud.pynew file+67
@@ -0,0 +1,67 @@
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)
searx/tests/engines/test_piratebay.pynew file+137
@@ -0,0 +1,137 @@
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)
searx/tests/engines/test_searchcode_code.pynew file+75
@@ -0,0 +1,75 @@
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)
searx/tests/engines/test_searchcode_doc.pynew file+73
@@ -0,0 +1,73 @@
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)
searx/tests/engines/test_soundcloud.pynew file+192
@@ -0,0 +1,192 @@
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)
searx/tests/engines/test_stackoverflow.pynew file+106
@@ -0,0 +1,106 @@
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)
searx/tests/engines/test_vimeo.pynew file+84
@@ -0,0 +1,84 @@
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)
searx/tests/engines/test_www500px.pynew file+83
@@ -0,0 +1,83 @@
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)
searx/tests/engines/test_youtube.pynew file+204
@@ -0,0 +1,204 @@
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)
searx/tests/test_engines.py+22
@@ -1,3 +1,25 @@
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
19from 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
212from searx.tests.engines.test_github import * # noqa
313from 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
searx/tests/test_utils.py+22
@@ -10,6 +10,11 @@ class TestUtils(SearxTestCase):
1010 self.assertIsNotNone(utils.gen_useragent())
1111 self.assertTrue(utils.gen_useragent().startswith('Mozilla'))
1212
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+
1318 def test_highlight_content(self):
1419 self.assertEqual(utils.highlight_content(0, None), None)
1520 self.assertEqual(utils.highlight_content(None, None), None)
@@ -29,6 +34,23 @@ class TestUtils(SearxTestCase):
2934 query = 'a test'
3035 self.assertEqual(utils.highlight_content(content, query), content)
3136
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+
3254
3355class TestHTMLTextExtractor(SearxTestCase):
3456
searx/utils.py+3−1
@@ -115,10 +115,12 @@ class HTMLTextExtractor(HTMLParser):
115115 self.result.append(name)
116116
117117 def get_text(self):
118− return u''.join(self.result)
118+ return u''.join(self.result).strip()
119119
120120
121121def html_to_text(html):
122+ html = html.replace('\n', ' ')
123+ html = ' '.join(html.split())
122124 s = HTMLTextExtractor()
123125 s.feed(html)
124126 return s.get_text()