2010-02-06 01:06:16 +03:00
|
|
|
# -*- coding: utf-8 -*-
|
2010-04-01 00:55:24 +04:00
|
|
|
from django import http
|
2010-02-06 01:06:16 +03:00
|
|
|
from django.core.cache import cache
|
2010-08-17 06:55:08 +04:00
|
|
|
from django.utils import http as urllib
|
2010-02-06 01:06:16 +03:00
|
|
|
|
2010-08-17 06:55:08 +04:00
|
|
|
import mock
|
2010-02-06 01:06:16 +03:00
|
|
|
from nose.tools import eq_, assert_raises
|
2010-05-05 20:56:10 +04:00
|
|
|
from pyquery import PyQuery as pq
|
2010-02-06 01:06:16 +03:00
|
|
|
|
|
|
|
import test_utils
|
|
|
|
|
2010-02-10 22:35:25 +03:00
|
|
|
import amo
|
2010-06-07 21:21:25 +04:00
|
|
|
import amo.test_utils
|
2010-08-21 00:48:17 +04:00
|
|
|
import addons.cron
|
2010-02-06 01:06:16 +03:00
|
|
|
from amo.urlresolvers import reverse
|
2010-02-10 22:35:25 +03:00
|
|
|
from amo.helpers import urlparams
|
2010-04-01 00:55:24 +04:00
|
|
|
from addons.models import Addon, Category
|
2010-08-17 06:55:08 +04:00
|
|
|
from browse import views, feeds
|
2010-02-06 01:06:16 +03:00
|
|
|
from browse.views import locale_display_name
|
2010-08-26 00:34:57 +04:00
|
|
|
from files.models import File
|
2010-08-17 06:55:08 +04:00
|
|
|
from translations.models import Translation
|
2010-06-02 22:44:43 +04:00
|
|
|
from translations.query import order_by_translation
|
2010-08-17 06:55:08 +04:00
|
|
|
from versions.models import Version
|
2010-02-06 01:06:16 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_locale_display_name():
|
2010-02-17 03:11:30 +03:00
|
|
|
|
2010-02-06 01:06:16 +03:00
|
|
|
def check(locale, english, native):
|
|
|
|
actual = locale_display_name(locale)
|
|
|
|
eq_(actual, (english, native))
|
|
|
|
|
|
|
|
check('el', 'Greek', u'Ελληνικά')
|
2010-05-05 21:21:50 +04:00
|
|
|
check('el-XX', 'Greek', u'Ελληνικά')
|
2010-02-06 01:06:16 +03:00
|
|
|
assert_raises(KeyError, check, 'fake-lang', '', '')
|
|
|
|
|
|
|
|
|
2010-06-29 01:26:53 +04:00
|
|
|
class TestLanguageTools(amo.test_utils.ExtraSetup, test_utils.TestCase):
|
2010-02-06 01:06:16 +03:00
|
|
|
fixtures = ['browse/test_views']
|
|
|
|
|
|
|
|
def setUp(self):
|
2010-05-21 22:01:11 +04:00
|
|
|
super(TestLanguageTools, self).setUp()
|
2010-02-06 01:06:16 +03:00
|
|
|
cache.clear()
|
2010-05-19 05:24:20 +04:00
|
|
|
self.url = reverse('browse.language-tools')
|
2010-02-17 02:22:39 +03:00
|
|
|
response = self.client.get(self.url, follow=True)
|
2010-07-01 20:29:18 +04:00
|
|
|
# For some reason the context doesn't get loaded the first time.
|
|
|
|
response = self.client.get(self.url, follow=True)
|
|
|
|
self.locales = list(response.context['locales'])
|
2010-02-06 01:06:16 +03:00
|
|
|
|
|
|
|
def test_sorting(self):
|
|
|
|
"""The locales should be sorted by English display name."""
|
2010-05-05 21:21:50 +04:00
|
|
|
displays = [locale.display for _, locale in self.locales]
|
2010-02-06 01:06:16 +03:00
|
|
|
eq_(displays, sorted(displays))
|
|
|
|
|
|
|
|
def test_native_missing_region(self):
|
|
|
|
"""
|
|
|
|
If we had to strip a locale's region to find a display name, we
|
|
|
|
append it to the native name for disambiguation.
|
|
|
|
"""
|
|
|
|
el = dict(self.locales)['el-XX']
|
|
|
|
assert el.native.endswith(' (el-xx)')
|
|
|
|
|
|
|
|
def test_missing_locale(self):
|
|
|
|
"""If we don't know about a locale, show the addon name and locale."""
|
|
|
|
wa = dict(self.locales)['wa']
|
|
|
|
eq_(wa.display, 'Walloon Language Pack (wa)')
|
|
|
|
eq_(wa.native, '')
|
|
|
|
|
|
|
|
def test_packs_and_dicts(self):
|
|
|
|
ca = dict(self.locales)['ca-valencia']
|
|
|
|
eq_(len(ca.dicts), 1)
|
2010-08-03 08:10:44 +04:00
|
|
|
eq_(len(ca.packs), 3)
|
2010-02-10 22:35:25 +03:00
|
|
|
|
2010-02-17 02:22:39 +03:00
|
|
|
def test_empty_target_locale(self):
|
|
|
|
"""Make sure nothing breaks with empty target locales."""
|
|
|
|
for addon in Addon.objects.all():
|
|
|
|
addon.target_locale = ''
|
|
|
|
addon.save()
|
|
|
|
response = self.client.get(self.url, follow=True)
|
|
|
|
eq_(response.status_code, 200)
|
2010-07-01 20:29:18 +04:00
|
|
|
eq_(list(response.context['locales']), [])
|
2010-02-17 02:22:39 +03:00
|
|
|
|
|
|
|
def test_null_target_locale(self):
|
|
|
|
"""Make sure nothing breaks with null target locales."""
|
|
|
|
for addon in Addon.objects.all():
|
|
|
|
addon.target_locale = None
|
|
|
|
addon.save()
|
|
|
|
response = self.client.get(self.url, follow=True)
|
|
|
|
eq_(response.status_code, 200)
|
2010-07-01 20:29:18 +04:00
|
|
|
eq_(list(response.context['locales']), [])
|
2010-02-17 02:22:39 +03:00
|
|
|
|
2010-02-10 22:35:25 +03:00
|
|
|
|
2010-06-07 21:21:25 +04:00
|
|
|
class TestThemes(amo.test_utils.ExtraSetup, test_utils.TestCase):
|
2010-08-12 01:02:59 +04:00
|
|
|
fixtures = ('base/category', 'base/addon_6704_grapple', 'base/addon_3615')
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
def setUp(self):
|
2010-05-19 05:24:20 +04:00
|
|
|
super(TestThemes, self).setUp()
|
2010-02-10 22:35:25 +03:00
|
|
|
# Make all the add-ons themes.
|
|
|
|
for addon in Addon.objects.all():
|
2010-06-07 21:20:39 +04:00
|
|
|
addon.type = amo.ADDON_THEME
|
2010-02-10 22:35:25 +03:00
|
|
|
addon.save()
|
2010-05-18 03:35:17 +04:00
|
|
|
for category in Category.objects.all():
|
2010-06-07 21:20:39 +04:00
|
|
|
category.type = amo.ADDON_THEME
|
2010-05-18 03:35:17 +04:00
|
|
|
category.save()
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
self.base_url = reverse('browse.themes')
|
2010-04-14 08:22:29 +04:00
|
|
|
self.exp_url = urlparams(self.base_url, unreviewed=True)
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
def test_default_sort(self):
|
2010-06-08 04:24:44 +04:00
|
|
|
"""Default sort should be by popular."""
|
2010-02-10 22:35:25 +03:00
|
|
|
response = self.client.get(self.base_url)
|
2010-06-08 04:24:44 +04:00
|
|
|
eq_(response.context['sorting'], 'popular')
|
2010-02-10 22:35:25 +03:00
|
|
|
|
2010-04-14 08:22:29 +04:00
|
|
|
def test_unreviewed(self):
|
|
|
|
# Only 3 without unreviewed.
|
2010-02-10 22:35:25 +03:00
|
|
|
response = self.client.get(self.base_url)
|
2010-08-12 01:02:59 +04:00
|
|
|
eq_(len(response.context['themes'].object_list), 2)
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
response = self.client.get(self.exp_url)
|
2010-08-12 01:02:59 +04:00
|
|
|
eq_(len(response.context['themes'].object_list), 2)
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
def _get_sort(self, sort):
|
|
|
|
response = self.client.get(urlparams(self.exp_url, sort=sort))
|
|
|
|
eq_(response.context['sorting'], sort)
|
|
|
|
return [a.id for a in response.context['themes'].object_list]
|
|
|
|
|
|
|
|
def test_download_sort(self):
|
2010-06-08 04:24:44 +04:00
|
|
|
ids = self._get_sort('popular')
|
2010-08-12 01:02:59 +04:00
|
|
|
eq_(ids, [3615, 6704])
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
def test_name_sort(self):
|
|
|
|
ids = self._get_sort('name')
|
2010-08-12 01:02:59 +04:00
|
|
|
eq_(ids, [3615, 6704])
|
2010-02-10 22:35:25 +03:00
|
|
|
|
2010-03-24 04:03:47 +03:00
|
|
|
def test_created_sort(self):
|
|
|
|
ids = self._get_sort('created')
|
2010-08-12 01:02:59 +04:00
|
|
|
eq_(ids, [6704, 3615])
|
2010-03-24 04:03:47 +03:00
|
|
|
|
|
|
|
def test_updated_sort(self):
|
|
|
|
ids = self._get_sort('updated')
|
2010-08-12 01:02:59 +04:00
|
|
|
eq_(ids, [6704, 3615])
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
def test_rating_sort(self):
|
|
|
|
ids = self._get_sort('rating')
|
2010-08-12 01:02:59 +04:00
|
|
|
eq_(ids, [6704, 3615])
|
2010-04-01 00:55:24 +04:00
|
|
|
|
2010-05-18 03:35:17 +04:00
|
|
|
def test_category_count(self):
|
2010-06-03 03:14:54 +04:00
|
|
|
cat = Category.objects.filter(name__isnull=False)[0]
|
2010-05-18 03:35:17 +04:00
|
|
|
response = self.client.get(reverse('browse.themes', args=[cat.slug]))
|
|
|
|
doc = pq(response.content)
|
|
|
|
actual_count = int(doc('hgroup h3').text().split()[0])
|
|
|
|
page = response.context['themes']
|
|
|
|
expected_count = page.paginator.count
|
|
|
|
eq_(actual_count, expected_count)
|
|
|
|
|
2010-04-01 00:55:24 +04:00
|
|
|
|
2010-06-07 21:21:25 +04:00
|
|
|
class TestCategoryPages(amo.test_utils.ExtraSetup, test_utils.TestCase):
|
2010-08-12 01:02:59 +04:00
|
|
|
fixtures = ('base/apps', 'base/category', 'base/addon_3615',
|
|
|
|
'base/featured', 'addons/featured', 'browse/nameless-addon')
|
2010-04-01 00:55:24 +04:00
|
|
|
|
2010-04-19 21:06:59 +04:00
|
|
|
def test_browsing_urls(self):
|
|
|
|
"""Every browse page URL exists."""
|
|
|
|
for _, slug in amo.ADDON_SLUGS.items():
|
2010-05-19 05:24:20 +04:00
|
|
|
assert reverse('browse.%s' % slug)
|
2010-04-19 21:06:59 +04:00
|
|
|
|
2010-04-01 00:55:24 +04:00
|
|
|
def test_matching_opts(self):
|
|
|
|
"""Every filter on landing pages is available on listing pages."""
|
|
|
|
for key, _ in views.CategoryLandingFilter.opts:
|
|
|
|
if key != 'featured':
|
|
|
|
assert key in dict(views.AddonFilter.opts)
|
|
|
|
|
2010-08-17 06:55:08 +04:00
|
|
|
@mock.patch('browse.views.category_landing')
|
2010-04-01 00:55:24 +04:00
|
|
|
def test_goto_category_landing(self, landing_mock):
|
|
|
|
"""We hit a landing page if there's a category and no sorting."""
|
|
|
|
landing_mock.return_value = http.HttpResponse()
|
|
|
|
|
|
|
|
self.client.get(reverse('browse.extensions'))
|
|
|
|
assert not landing_mock.called
|
|
|
|
|
|
|
|
slug = Category.objects.all()[0].slug
|
|
|
|
category_url = reverse('browse.extensions', args=[slug])
|
|
|
|
self.client.get('%s?sort=created' % category_url)
|
|
|
|
assert not landing_mock.called
|
|
|
|
|
|
|
|
self.client.get(category_url)
|
|
|
|
assert landing_mock.called
|
2010-04-09 05:42:02 +04:00
|
|
|
|
|
|
|
def test_creatured_addons(self):
|
|
|
|
"""Make sure the creatured add-ons are for the right category."""
|
|
|
|
# Featured in bookmarks.
|
|
|
|
url = reverse('browse.extensions', args=['bookmarks'])
|
|
|
|
response = self.client.get(url, follow=True)
|
|
|
|
creatured = response.context['filter'].all()['featured']
|
|
|
|
eq_(len(creatured), 1)
|
|
|
|
eq_(creatured[0].id, 3615)
|
|
|
|
|
|
|
|
# Not featured in search-tools.
|
|
|
|
url = reverse('browse.extensions', args=['search-tools'])
|
|
|
|
response = self.client.get(url, follow=True)
|
|
|
|
creatured = response.context['filter'].all()['featured']
|
|
|
|
eq_(len(creatured), 0)
|
2010-05-05 20:56:10 +04:00
|
|
|
|
2010-05-29 01:31:42 +04:00
|
|
|
def test_creatured_only_public(self):
|
|
|
|
"""Make sure the creatured add-ons are all public."""
|
|
|
|
url = reverse('browse.creatured', args=['bookmarks'])
|
|
|
|
r = self.client.get(url, follow=True)
|
|
|
|
addons = r.context['addons']
|
|
|
|
|
|
|
|
for a in addons:
|
|
|
|
assert a.status == amo.STATUS_PUBLIC, "%s is not public" % a.name
|
|
|
|
|
|
|
|
old_count = len(addons)
|
|
|
|
addons[0].status = amo.STATUS_UNREVIEWED
|
|
|
|
addons[0].save()
|
|
|
|
r = self.client.get(url, follow=True)
|
|
|
|
addons = r.context['addons']
|
|
|
|
|
|
|
|
for a in addons:
|
|
|
|
assert a.status == amo.STATUS_PUBLIC, ("Altered %s is featured"
|
|
|
|
% a.name)
|
|
|
|
|
|
|
|
eq_(len(addons), old_count - 1, "The number of addons is the same.")
|
|
|
|
|
2010-05-05 20:56:10 +04:00
|
|
|
def test_added_date(self):
|
|
|
|
url = reverse('browse.extensions') + '?sort=created'
|
|
|
|
doc = pq(self.client.get(url).content)
|
|
|
|
s = doc('.featured .item .updated').text()
|
|
|
|
assert s.strip().startswith('Added'), s
|
2010-05-19 05:24:20 +04:00
|
|
|
|
2010-06-02 22:44:43 +04:00
|
|
|
def test_sorting_nameless(self):
|
|
|
|
"""Nameless add-ons are dropped from the sort."""
|
|
|
|
qs = Addon.objects.all()
|
|
|
|
ids = order_by_translation(qs, 'name')
|
|
|
|
assert 57132 in [a.id for a in qs]
|
|
|
|
assert 57132 not in [a.id for a in ids]
|
|
|
|
|
2010-08-26 00:34:57 +04:00
|
|
|
def test_jetpack_listing(self):
|
|
|
|
x = File.objects.get(pk=67442)
|
|
|
|
x.jetpack = True
|
|
|
|
x.save()
|
|
|
|
|
|
|
|
url = reverse('browse.extensions') + '?sort=created&jetpack=on'
|
|
|
|
doc = pq(self.client.get(url).content)
|
|
|
|
eq_(len(doc('.item')), 1)
|
|
|
|
|
2010-05-19 05:24:20 +04:00
|
|
|
|
|
|
|
class TestLegacyRedirects(test_utils.TestCase):
|
2010-08-12 01:02:59 +04:00
|
|
|
fixtures = ('base/category.json',)
|
2010-05-19 05:24:20 +04:00
|
|
|
|
|
|
|
def test_types(self):
|
|
|
|
def redirects(from_, to):
|
2010-05-21 22:01:11 +04:00
|
|
|
r = self.client.get('/en-US/firefox' + from_)
|
2010-08-12 01:02:59 +04:00
|
|
|
self.assertRedirects(r, '/en-US/firefox' + to, status_code=301,
|
|
|
|
msg_prefix="Redirection failed: %s" % to)
|
2010-05-19 05:24:20 +04:00
|
|
|
|
|
|
|
redirects('/browse/type:1', '/extensions/')
|
|
|
|
redirects('/browse/type:1/', '/extensions/')
|
|
|
|
redirects('/browse/type:1/cat:all', '/extensions/')
|
|
|
|
redirects('/browse/type:1/cat:all/', '/extensions/')
|
|
|
|
redirects('/browse/type:1/cat:72', '/extensions/alerts-updates/')
|
|
|
|
redirects('/browse/type:1/cat:72/', '/extensions/alerts-updates/')
|
|
|
|
|
|
|
|
redirects('/browse/type:2', '/themes/')
|
2010-06-03 03:13:38 +04:00
|
|
|
redirects('/browse/type:3', '/language-tools/')
|
2010-06-19 03:11:12 +04:00
|
|
|
redirects('/browse/type:4', '/search-tools/')
|
|
|
|
redirects('/search-engines', '/search-tools/')
|
2010-05-19 05:24:20 +04:00
|
|
|
# redirects('/browse/type:7', '/plugins/')
|
2010-08-02 05:17:36 +04:00
|
|
|
redirects('/recommended', '/featured')
|
2010-08-06 08:32:19 +04:00
|
|
|
redirects('/recommended/format:rss', '/featured/format:rss')
|
2010-08-02 05:17:36 +04:00
|
|
|
|
2010-08-21 00:48:17 +04:00
|
|
|
|
2010-08-02 05:17:36 +04:00
|
|
|
class TestFeaturedPage(amo.test_utils.ExtraSetup, test_utils.TestCase):
|
2010-08-12 01:02:59 +04:00
|
|
|
fixtures = ('base/apps', 'addons/featured')
|
2010-08-02 05:17:36 +04:00
|
|
|
|
|
|
|
def test_featured_addons(self):
|
|
|
|
"""Make sure that only featured add-ons are shown"""
|
|
|
|
|
|
|
|
response = self.client.get(reverse('browse.featured'))
|
2010-08-19 02:10:21 +04:00
|
|
|
eq_([1001, 1003], sorted(a.id for a in response.context['addons']))
|
2010-08-17 06:55:08 +04:00
|
|
|
|
2010-08-21 00:48:17 +04:00
|
|
|
|
2010-08-06 08:32:19 +04:00
|
|
|
class TestCategoriesFeed(test_utils.TestCase):
|
2010-08-17 06:55:08 +04:00
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.feed = feeds.CategoriesRss()
|
|
|
|
self.u = u'Ελληνικά'
|
|
|
|
self.wut = Translation(localized_string=self.u, locale='el')
|
|
|
|
|
|
|
|
self.feed.request = mock.Mock()
|
|
|
|
self.feed.request.APP.pretty = self.u
|
|
|
|
|
|
|
|
self.category = Category(name=self.u)
|
|
|
|
|
|
|
|
self.addon = Addon(name=self.u, id=2)
|
|
|
|
self.addon._current_version = Version(version='v%s' % self.u)
|
|
|
|
|
|
|
|
def test_title(self):
|
|
|
|
eq_(self.feed.title(self.category),
|
|
|
|
u'%s :: Add-ons for %s' % (self.wut, self.u))
|
|
|
|
|
|
|
|
def test_item_title(self):
|
|
|
|
eq_(self.feed.item_title(self.addon),
|
|
|
|
u'%s v%s' % (self.u, self.u))
|
|
|
|
|
|
|
|
def test_item_guid(self):
|
|
|
|
t = self.feed.item_guid(self.addon)
|
|
|
|
assert t.endswith(u'/addon/2/versions/v%s' % urllib.urlquote(self.u))
|
2010-08-06 08:32:19 +04:00
|
|
|
|
2010-08-21 00:48:17 +04:00
|
|
|
|
2010-08-06 08:32:19 +04:00
|
|
|
class TestFeaturedFeed(amo.test_utils.ExtraSetup, test_utils.TestCase):
|
|
|
|
fixtures = ('base/apps', 'addons/featured')
|
|
|
|
|
|
|
|
def test_feed_elements_present(self):
|
|
|
|
"""specific elements are present and reasonably well formed"""
|
|
|
|
url = reverse('browse.featured.rss')
|
|
|
|
r = self.client.get(url, follow=True)
|
|
|
|
doc = pq(r.content)
|
|
|
|
eq_(doc('rss channel title')[0].text,
|
|
|
|
'Featured Add-ons :: Add-ons for Firefox')
|
|
|
|
assert doc('rss channel link')[0].text.endswith('/en-US/firefox/')
|
|
|
|
eq_(doc('rss channel description')[0].text,
|
|
|
|
"Here's a few of our favorite add-ons to help you get " \
|
|
|
|
"started customizing Firefox.")
|
2010-08-21 01:58:37 +04:00
|
|
|
eq_(len(doc('rss channel item')), 2)
|
2010-08-28 04:22:07 +04:00
|
|
|
|
|
|
|
|
|
|
|
class TestPersonas(amo.test_utils.ExtraSetup, test_utils.TestCase):
|
|
|
|
fixtures = ('base/apps', 'addons/featured')
|
|
|
|
|
|
|
|
def test_personas(self):
|
|
|
|
eq_(self.client.get(reverse('browse.personas')).status_code, 200)
|