477 строки
15 KiB
Python
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 '<script>' 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&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="<script>"><script></a></li></ul>'),
|
|
copyright: (
|
|
'<ul class="license"><li class="icon <script>"></li>'
|
|
'<li class="text"><script></li></ul>'),
|
|
cc: (
|
|
'<ul class="license"><li class="icon <script>"></li>'
|
|
'<li class="icon cc-noncom"></li><li class="icon cc-share">'
|
|
'</li><li class="text"><a href="<script>" '
|
|
'title="<script>">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')
|