addons-server/apps/amo/tests/test_helpers.py

477 строки
15 KiB
Python

# -*- coding: utf-8 -*-
from datetime import datetime
import mimetypes
import os
from django.conf import settings
from django.core import mail
from django.core.cache import cache
from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils import encoding
import jingo
import test_utils
from mock import Mock, patch
from nose.tools import eq_
from pyquery import PyQuery
import amo
import amo.tests
from amo import urlresolvers, utils, helpers
from amo.utils import guard, ImageCheck, Message, Token
from versions.models import License
def render(s, context={}):
t = jingo.env.from_string(s)
return t.render(**context)
def test_strip_html():
eq_('Hey Brother!', render('{{ "Hey <b>Brother!</b>"|strip_html }}'))
def test_currencyfmt():
eq_(helpers.currencyfmt(None, 'USD'), '')
eq_(helpers.currencyfmt(5, 'USD'), '$5.00')
def test_strip_html_none():
eq_('', render('{{ a|strip_html }}', {'a': None}))
eq_('', render('{{ a|strip_html(True) }}', {'a': None}))
def test_strip_controls():
# We want control codes like \x0c to disappear.
eq_('I ove you', helpers.strip_controls('I \x0cove you'))
def test_finalize():
"""We want None to show up as ''. We do this in JINJA_CONFIG."""
eq_('', render('{{ x }}', {'x': None}))
def test_slugify_spaces():
"""We want slugify to preserve spaces, but not at either end."""
eq_(utils.slugify(' b ar '), 'b-ar')
eq_(utils.slugify(' b ar ', spaces=True), 'b ar')
eq_(utils.slugify(' b ar ', spaces=True), 'b ar')
def test_page_title():
request = Mock()
request.APP = amo.THUNDERBIRD
title = 'Oh hai!'
s = render('{{ page_title("%s") }}' % title, {'request': request})
eq_(s, '%s :: Add-ons for Thunderbird' % title)
# pages without app should show a default
request.APP = None
s = render('{{ page_title("%s") }}' % title, {'request': request})
eq_(s, '%s :: Add-ons' % title)
# Check the dirty unicodes.
request.APP = amo.FIREFOX
s = render('{{ page_title(x) }}',
{'request': request,
'x': encoding.smart_str(u'\u05d0\u05d5\u05e1\u05e3')})
class TestBreadcrumbs(object):
def setUp(self):
self.req_noapp = Mock()
self.req_noapp.APP = None
self.req_app = Mock()
self.req_app.APP = amo.FIREFOX
def test_no_app(self):
s = render('{{ breadcrumbs() }}', {'request': self.req_noapp})
doc = PyQuery(s)
crumbs = doc('li>a')
eq_(len(crumbs), 1)
eq_(crumbs.text(), 'Add-ons')
eq_(crumbs.attr('href'), urlresolvers.reverse('home'))
def test_with_app(self):
s = render('{{ breadcrumbs() }}', {'request': self.req_app})
doc = PyQuery(s)
crumbs = doc('li>a')
eq_(len(crumbs), 1)
eq_(crumbs.text(), 'Add-ons for Firefox')
eq_(crumbs.attr('href'), urlresolvers.reverse('home'))
def test_no_add_default(self):
s = render('{{ breadcrumbs(add_default=False) }}',
{'request': self.req_app})
eq_(len(s), 0)
def test_items(self):
s = render("""{{ breadcrumbs([('/foo', 'foo'),
('/bar', 'bar')],
add_default=False) }}'""",
{'request': self.req_app})
doc = PyQuery(s)
crumbs = doc('li>a')
eq_(len(crumbs), 2)
eq_(crumbs.eq(0).text(), 'foo')
eq_(crumbs.eq(0).attr('href'), '/foo')
eq_(crumbs.eq(1).text(), 'bar')
eq_(crumbs.eq(1).attr('href'), '/bar')
def test_items_with_default(self):
s = render("""{{ breadcrumbs([('/foo', 'foo'),
('/bar', 'bar')]) }}'""",
{'request': self.req_app})
doc = PyQuery(s)
crumbs = doc('li>a')
eq_(len(crumbs), 3)
eq_(crumbs.eq(1).text(), 'foo')
eq_(crumbs.eq(1).attr('href'), '/foo')
eq_(crumbs.eq(2).text(), 'bar')
eq_(crumbs.eq(2).attr('href'), '/bar')
def test_truncate(self):
s = render("""{{ breadcrumbs([('/foo', 'abcd efghij'),],
crumb_size=5) }}'""",
{'request': self.req_app})
doc = PyQuery(s)
crumbs = doc('li>a')
eq_('abcd ...', crumbs.eq(1).text())
def test_xss(self):
s = render("{{ breadcrumbs([('/foo', '<script>')]) }}",
{'request': self.req_app})
assert '&lt;script&gt;' in s, s
assert '<script>' not in s
@patch('amo.helpers.urlresolvers.reverse')
def test_url(mock_reverse):
render('{{ url("viewname", 1, z=2) }}')
mock_reverse.assert_called_with('viewname', args=(1,), kwargs={'z': 2},
add_prefix=True)
render('{{ url("viewname", 1, z=2, host="myhost") }}')
mock_reverse.assert_called_with('viewname', args=(1,), kwargs={'z': 2},
add_prefix=True)
def test_url_src():
s = render('{{ url("addons.detail", "a3615", src="xxx") }}')
assert s.endswith('?src=xxx')
def test_urlparams():
url = '/en-US/firefox/themes/category'
c = {'base': url,
'base_frag': url + '#hash',
'base_query': url + '?x=y',
'sort': 'name', 'frag': 'frag'}
# Adding a query.
s = render('{{ base_frag|urlparams(sort=sort) }}', c)
eq_(s, '%s?sort=name#hash' % url)
# Adding a fragment.
s = render('{{ base|urlparams(frag) }}', c)
eq_(s, '%s#frag' % url)
# Replacing a fragment.
s = render('{{ base_frag|urlparams(frag) }}', c)
eq_(s, '%s#frag' % url)
# Adding query and fragment.
s = render('{{ base_frag|urlparams(frag, sort=sort) }}', c)
eq_(s, '%s?sort=name#frag' % url)
# Adding query with existing params.
s = render('{{ base_query|urlparams(frag, sort=sort) }}', c)
eq_(s, '%s?sort=name&amp;x=y#frag' % url)
# Replacing a query param.
s = render('{{ base_query|urlparams(frag, x="z") }}', c)
eq_(s, '%s?x=z#frag' % url)
# Params with value of None get dropped.
s = render('{{ base|urlparams(sort=None) }}', c)
eq_(s, url)
# Removing a query
s = render('{{ base_query|urlparams(x=None) }}', c)
eq_(s, url)
def test_urlparams_unicode():
url = u'/xx?evil=reco\ufffd\ufffd\ufffd\u02f5'
utils.urlparams(url)
class TestSharedURL(amo.tests.TestCase):
def setUp(self):
self.webapp = Mock()
self.webapp.type = amo.ADDON_WEBAPP
self.webapp.app_slug = 'webapp'
self.addon = Mock()
self.addon.type = amo.ADDON_EXTENSION
self.addon.slug = 'addon'
self.addon.is_webapp.return_value = False
def test_appurl(self):
expected = '/en-US/apps/app/webapp/'
eq_(helpers.shared_url('addons.detail', self.webapp), expected)
eq_(helpers.shared_url('apps.detail', self.webapp), expected)
eq_(helpers.shared_url('detail', self.webapp), expected)
eq_(helpers.shared_url('detail', self.webapp, add_prefix=False),
'/apps/app/webapp/')
eq_(helpers.shared_url('reviews.detail', self.webapp, 1,
add_prefix=False),
'/apps/app/webapp/reviews/1/')
def test_addonurl(self):
expected = '/en-US/firefox/addon/addon/'
eq_(helpers.shared_url('addons.detail', self.addon), expected)
eq_(helpers.shared_url('apps.detail', self.addon), expected)
eq_(helpers.shared_url('detail', self.addon), expected)
eq_(helpers.shared_url('detail', self.addon, add_prefix=False),
'/addon/addon/')
eq_(helpers.shared_url('reviews.detail', self.addon, 1,
add_prefix=False),
'/addon/addon/reviews/1/')
def test_isotime():
time = datetime(2009, 12, 25, 10, 11, 12)
s = render('{{ d|isotime }}', {'d': time})
eq_(s, '2009-12-25T18:11:12Z')
s = render('{{ d|isotime }}', {'d': None})
eq_(s, '')
def test_epoch():
time = datetime(2009, 12, 25, 10, 11, 12)
s = render('{{ d|epoch }}', {'d': time})
eq_(s, '1261764672')
s = render('{{ d|epoch }}', {'d': None})
eq_(s, '')
def test_locale_url():
rf = test_utils.RequestFactory()
request = rf.get('/de', SCRIPT_NAME='/z')
prefixer = urlresolvers.Prefixer(request)
urlresolvers.set_url_prefix(prefixer)
s = render('{{ locale_url("mobile") }}')
eq_(s, '/z/de/mobile')
def test_external_url():
redirect_url = settings.REDIRECT_URL
secretkey = settings.REDIRECT_SECRET_KEY
settings.REDIRECT_URL = 'http://example.net'
settings.REDIRECT_SECRET_KEY = 'sekrit'
try:
myurl = 'http://example.com'
s = render('{{ "%s"|external_url }}' % myurl)
eq_(s, urlresolvers.get_outgoing_url(myurl))
finally:
settings.REDIRECT_URL = redirect_url
settings.REDIRECT_SECRET_KEY = secretkey
class TestLicenseLink(amo.tests.TestCase):
def test_license_link(self):
mit = License.objects.create(
name='MIT/X11 License', builtin=6, url='http://m.it')
copyright = License.objects.create(
name='All Rights Reserved', icons='copyr', builtin=7)
cc = License.objects.create(
name='Creative Commons', url='http://cre.at', builtin=8,
some_rights=True, icons='cc-attrib cc-noncom cc-share')
cc.save()
expected = {
mit: (
'<ul class="license"><li class="text">'
'<a href="http://m.it">MIT/X11 License</a></li></ul>'),
copyright: (
'<ul class="license"><li class="icon copyr"></li>'
'<li class="text">All Rights Reserved</li></ul>'),
cc: (
'<ul class="license"><li class="icon cc-attrib"></li>'
'<li class="icon cc-noncom"></li><li class="icon cc-share">'
'</li><li class="text"><a href="http://cre.at" '
'title="Creative Commons">Some rights reserved</a></li></ul>'),
}
for lic, ex in expected.items():
s = render('{{ license_link(lic) }}', {'lic': lic})
s = ''.join([s.strip() for s in s.split('\n')])
eq_(s, ex)
def test_license_link_xss(self):
mit = License.objects.create(
name='<script>', builtin=6, url='<script>')
copyright = License.objects.create(
name='<script>', icons='<script>', builtin=7)
cc = License.objects.create(
name='<script>', url='<script>', builtin=8,
some_rights=True, icons='<script> cc-noncom cc-share')
cc.save()
expected = {
mit: (
'<ul class="license"><li class="text">'
'<a href="&lt;script&gt;">&lt;script&gt;</a></li></ul>'),
copyright: (
'<ul class="license"><li class="icon &lt;script&gt;"></li>'
'<li class="text">&lt;script&gt;</li></ul>'),
cc: (
'<ul class="license"><li class="icon &lt;script&gt;"></li>'
'<li class="icon cc-noncom"></li><li class="icon cc-share">'
'</li><li class="text"><a href="&lt;script&gt;" '
'title="&lt;script&gt;">Some rights reserved</a></li></ul>'),
}
for lic, ex in expected.items():
s = render('{{ license_link(lic) }}', {'lic': lic})
s = ''.join([s.strip() for s in s.split('\n')])
eq_(s, ex)
def get_image_path(name):
return os.path.join(settings.ROOT, 'apps', 'amo', 'tests', 'images', name)
def get_uploaded_file(name):
data = open(get_image_path(name)).read()
return SimpleUploadedFile(name, data,
content_type=mimetypes.guess_type(name)[0])
class TestAnimatedImages(amo.tests.TestCase):
def test_animated_images(self):
img = ImageCheck(open(get_image_path('animated.png')))
assert img.is_animated()
img = ImageCheck(open(get_image_path('non-animated.png')))
assert not img.is_animated()
img = ImageCheck(open(get_image_path('animated.gif')))
assert img.is_animated()
img = ImageCheck(open(get_image_path('non-animated.gif')))
assert not img.is_animated()
def test_junk(self):
img = ImageCheck(open(__file__, 'rb'))
assert not img.is_image()
img = ImageCheck(open(get_image_path('non-animated.gif')))
assert img.is_image()
class TestToken(amo.tests.TestCase):
def test_token_pop(self):
new = Token()
new.save()
assert Token.pop(new.token)
assert not Token.pop(new.token)
def test_token_valid(self):
new = Token()
new.save()
assert Token.valid(new.token)
def test_token_fails(self):
assert not Token.pop('some-random-token')
def test_token_ip(self):
new = Token(data='127.0.0.1')
new.save()
assert Token.valid(new.token, '127.0.0.1')
def test_token_no_ip_invalid(self):
new = Token()
assert not Token.valid(new.token, '255.255.255.0')
def test_token_bad_ip_invalid(self):
new = Token(data='127.0.0.1')
new.save()
assert not Token.pop(new.token, '255.255.255.0')
assert Token.pop(new.token, '127.0.0.1')
def test_token_well_formed(self):
new = Token('some badly formed token')
assert not new.well_formed()
class TestMessage(amo.tests.TestCase):
def test_message_save(self):
new = Message('abc')
new.save('123')
new = Message('abc')
eq_(new.get(), '123')
def test_message_expires(self):
new = Message('abc')
new.save('123')
cache.clear()
new = Message('abc')
eq_(new.get(), None)
def test_message_get_delete(self):
new = Message('abc')
new.save('123')
new = Message('abc')
eq_(new.get(delete=False), '123')
eq_(new.get(delete=True), '123')
eq_(new.get(), None)
def test_guard(self):
with guard('abc') as locked:
eq_(locked, False)
eq_(Message('abc').get(), True)
def test_guard_copes(self):
try:
with guard('abc'):
1/0
except ZeroDivisionError:
pass
eq_(Message('abc').get(), None)
def test_guard_deletes(self):
with guard('abc'):
pass
eq_(Message('abc').get(), None)
def test_guard_blocks(self):
Message('abc').save(True)
with guard('abc') as locked:
eq_(locked, True)
def test_site_nav():
r = Mock()
r.APP = amo.FIREFOX
assert 'id="site-nav"' in helpers.site_nav({'request': r})
def test_jinja_trans_monkeypatch():
# This tests the monkeypatch in manage.py that prevents localizers from
# taking us down.
render('{% trans come_on=1 %}% (come_on)s{% endtrans %}')
render('{% trans come_on=1 %}%(come_on){% endtrans %}')
render('{% trans come_on=1 %}%(come_on)z{% endtrans %}')
def test_absolutify():
eq_(helpers.absolutify('/woo'), settings.SITE_URL + '/woo')
eq_(helpers.absolutify('https://addons.mozilla.org'),
'https://addons.mozilla.org')