addons-server/apps/devhub/tests/test_views.py

3587 строки
136 KiB
Python
Исходник Обычный вид История

# -*- coding: utf8 -*-
import json
import os
import re
2010-10-11 22:24:41 +04:00
import socket
2010-12-30 20:54:28 +03:00
import tempfile
from datetime import datetime, timedelta
2010-11-06 00:51:28 +03:00
from decimal import Decimal
from collections import namedtuple
2010-10-08 07:08:41 +04:00
from django.conf import settings
from django.core.cache import cache
2010-09-30 07:12:18 +04:00
from django.utils import translation
from django.utils.http import urlencode
2010-10-11 22:24:41 +04:00
import mock
2010-11-18 00:41:34 +03:00
from nose.tools import eq_, assert_not_equal, assert_raises
from nose.plugins.attrib import attr
from PIL import Image
from pyquery import PyQuery as pq
import test_utils
import waffle
import amo
2010-10-11 22:24:41 +04:00
import paypal
from amo.helpers import url as url_reverse
from amo.tests import (formset, initial, close_to_now,
assert_no_validation_errors)
2010-12-10 22:52:26 +03:00
from amo.tests.test_helpers import get_image_path
from amo.urlresolvers import reverse
from addons import cron
from addons.forms import AddonFormBasic
from addons.models import Addon, AddonUser, Charity, Category, AddonCategory
2010-11-23 04:45:12 +03:00
from addons.utils import ReverseNameLookup
2010-12-01 23:45:43 +03:00
from applications.models import Application, AppVersion
from bandwagon.models import Collection, CollectionAddon, FeaturedCollection
2011-02-23 21:25:08 +03:00
from devhub.forms import ContribForm
2011-06-02 04:47:19 +04:00
from devhub.models import ActivityLog, BlogPost, SubmitStep
from devhub import tasks
import files
from files.models import File, FileUpload, Platform
from files.tests.test_models import UploadTest as BaseUploadTest
from reviews.models import Review
from tags.models import Tag, AddonTag
from translations.models import Translation
from users.models import UserProfile
from versions.models import ApplicationsVersions, License, Version
2011-04-17 03:37:09 +04:00
class MetaTests(test_utils.TestCase):
def test_assert_close_to_now(dt):
assert close_to_now(datetime.now() - timedelta(seconds=30))
assert not close_to_now(datetime.now() + timedelta(days=30))
assert not close_to_now(datetime.now() + timedelta(minutes=3))
assert not close_to_now(datetime.now() + timedelta(seconds=30))
class HubTest(test_utils.TestCase):
fixtures = ['browse/nameless-addon', 'base/users']
def setUp(self):
translation.activate('en-US')
self.url = reverse('devhub.index')
self.login_as_developer()
eq_(self.client.get(self.url).status_code, 200)
self.user_profile = UserProfile.objects.get(id=999)
def login_as_developer(self):
self.client.login(username='regular@mozilla.com', password='password')
def clone_addon(self, num, addon_id=57132):
ids = []
2010-12-31 04:02:31 +03:00
for i in range(num):
addon = Addon.objects.get(id=addon_id)
addon.id = addon.guid = None
addon.save()
AddonUser.objects.create(user=self.user_profile, addon=addon)
new_addon = Addon.objects.get(id=addon.id)
new_addon.name = str(addon.id)
new_addon.save()
ids.append(addon.id)
return ids
class TestNav(HubTest):
def test_navbar(self):
r = self.client.get(self.url)
doc = pq(r.content)
2011-06-10 07:29:59 +04:00
eq_(doc('#site-nav').length, 1)
def test_no_addons(self):
"""Check that no add-ons are displayed for this user."""
r = self.client.get(self.url)
doc = pq(r.content)
assert_not_equal(doc('#navbar ul li.top a').eq(0).text(),
'My Add-ons',
'My Add-ons menu should not be visible if user has no add-ons.')
def test_my_addons(self):
"""Check that the correct items are listed for the My Add-ons menu."""
# Assign this add-on to the current user profile.
addon = Addon.objects.get(id=57132)
AddonUser.objects.create(user=self.user_profile, addon=addon)
r = self.client.get(self.url)
doc = pq(r.content)
# Check the anchor for the 'My Add-ons' menu item.
2011-06-10 07:29:59 +04:00
eq_(doc('#site-nav ul li.top a').eq(0).text(), 'My Add-ons')
# Check the anchor for the single add-on.
edit_url = reverse('devhub.addons.edit', args=[addon.slug])
2011-06-10 07:29:59 +04:00
eq_(doc('#site-nav ul li.top li a').eq(0).attr('href'), edit_url)
# Create 6 add-ons.
self.clone_addon(6)
r = self.client.get(self.url)
doc = pq(r.content)
# There should be 8 items in this menu.
2011-06-10 07:29:59 +04:00
eq_(doc('#site-nav ul li.top').eq(0).find('ul li').length, 8)
# This should be the 8th anchor, after the 7 addons.
2011-06-10 07:29:59 +04:00
eq_(doc('#site-nav ul li.top').eq(0).find('li a').eq(7).text(),
'Submit a New Add-on')
self.clone_addon(1)
r = self.client.get(self.url)
doc = pq(r.content)
2011-06-10 07:29:59 +04:00
eq_(doc('#site-nav ul li.top').eq(0).find('li a').eq(7).text(),
'more add-ons...')
class TestDashboard(HubTest):
def setUp(self):
super(TestDashboard, self).setUp()
self.url = reverse('devhub.addons')
eq_(self.client.get(self.url).status_code, 200)
def get_action_links(self, addon_id):
r = self.client.get(self.url)
doc = pq(r.content)
links = [a.text.strip() for a in
doc('.item[data-addonid=%s] .item-actions li > a' % addon_id)]
return links
def test_no_addons(self):
"""Check that no add-ons are displayed for this user."""
r = self.client.get(self.url)
doc = pq(r.content)
eq_(doc('.item item').length, 0)
def test_addon_pagination(self):
"""Check that the correct info. is displayed for each add-on:
namely, that add-ons are paginated at 10 items per page, and that
when there is more than one page, the 'Sort by' header and pagination
footer appear.
"""
# Create 10 add-ons.
self.clone_addon(10)
r = self.client.get(self.url)
doc = pq(r.content)
eq_(len(doc('.item .item-info')), 10)
eq_(doc('#addon-list-options').length, 0)
eq_(doc('.listing-footer .pagination').length, 0)
# Create 5 add-ons.
self.clone_addon(5)
r = self.client.get(self.url + '?page=2')
doc = pq(r.content)
eq_(len(doc('.item .item-info')), 5)
eq_(doc('#addon-list-options').length, 1)
eq_(doc('.listing-footer .pagination').length, 1)
2010-09-30 07:12:18 +04:00
def test_show_hide_statistics(self):
a_pk = self.clone_addon(1)[0]
# when Active and Public show statistics
Addon.objects.get(pk=a_pk).update(disabled_by_user=False,
status=amo.STATUS_PUBLIC)
links = self.get_action_links(a_pk)
assert 'Statistics' in links, ('Unexpected: %r' % links)
# when Active and Incomplete hide statistics
Addon.objects.get(pk=a_pk).update(disabled_by_user=False,
status=amo.STATUS_NULL)
links = self.get_action_links(a_pk)
assert 'Statistics' not in links, ('Unexpected: %r' % links)
def test_complete_addon_item(self):
a_pk = self.clone_addon(1)[0]
a = Addon.objects.get(pk=a_pk)
r = self.client.get(self.url)
doc = pq(r.content)
eq_(a.status, amo.STATUS_PUBLIC)
assert doc('.item[data-addonid=%s] ul.item-details' % a_pk)
assert doc('.item[data-addonid=%s] h4 a' % a_pk)
assert not doc('.item[data-addonid=%s] > p' % a_pk)
def test_incomplete_addon_item(self):
a_pk = self.clone_addon(1)[0]
Addon.objects.get(pk=a_pk).update(status=amo.STATUS_NULL)
r = self.client.get(self.url)
doc = pq(r.content)
assert not doc('.item[data-addonid=%s] ul.item-details' % a_pk)
assert not doc('.item[data-addonid=%s] h4 a' % a_pk)
assert doc('.item[data-addonid=%s] > p' % a_pk)
2011-06-02 04:47:19 +04:00
def test_dev_news(self):
self.clone_addon(1) # We need one to see this module
for i in xrange(7):
bp = BlogPost(title='hi %s' % i,
date_posted=datetime.now() - timedelta(days=i))
bp.save()
r = self.client.get(self.url)
doc = pq(r.content)
eq_(doc('.blog-posts').length, 1)
eq_(doc('.blog-posts li').length, 5)
eq_(doc('.blog-posts li a').eq(0).text(), "hi 0")
eq_(doc('.blog-posts li a').eq(4).text(), "hi 4")
2010-09-30 07:12:18 +04:00
class TestUpdateCompatibility(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_4594_a9',
'base/addon_3615']
def setUp(self):
self.url = reverse('devhub.addons')
2011-05-25 21:58:00 +04:00
# TODO(andym): use Mock appropriately here.
self.old_version = amo.FIREFOX.latest_version
amo.FIREFOX.latest_version = '3.6.15'
def tearDown(self):
amo.FIREFOX.latest_version = self.old_version
def test_no_compat(self):
assert self.client.login(username='admin@mozilla.com',
password='password')
r = self.client.get(self.url)
doc = pq(r.content)
assert not doc('.item[data-addonid=4594] li.compat')
a = Addon.objects.get(pk=4594)
r = self.client.get(reverse('devhub.ajax.compat.update',
args=[a.slug, a.current_version.id]))
eq_(r.status_code, 404)
r = self.client.get(reverse('devhub.ajax.compat.status',
args=[a.slug]))
eq_(r.status_code, 404)
def test_compat(self):
a = Addon.objects.get(pk=3615)
assert self.client.login(username='del@icio.us', password='password')
r = self.client.get(self.url)
doc = pq(r.content)
cu = doc('.item[data-addonid=3615] .tooltip.compat-update')
assert cu
update_url = reverse('devhub.ajax.compat.update',
args=[a.slug, a.current_version.id])
eq_(cu.attr('data-updateurl'), update_url)
status_url = reverse('devhub.ajax.compat.status', args=[a.slug])
eq_(doc('.item[data-addonid=3615] li.compat').attr('data-src'),
status_url)
assert doc('.item[data-addonid=3615] .compat-update-modal')
def test_incompat(self):
av = ApplicationsVersions.objects.get(pk=47881)
av.max = AppVersion.objects.get(pk=97) # Firefox 2.0
av.save()
assert self.client.login(username='del@icio.us', password='password')
r = self.client.get(self.url)
doc = pq(r.content)
assert doc('.item[data-addonid=3615] .tooltip.compat-error')
class TestDevRequired(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
self.get_url = reverse('devhub.addons.payments', args=['a3615'])
self.post_url = reverse('devhub.addons.payments.disable',
args=['a3615'])
assert self.client.login(username='del@icio.us', password='password')
self.addon = Addon.objects.get(id=3615)
self.au = AddonUser.objects.get(user__email='del@icio.us',
addon=self.addon)
eq_(self.au.role, amo.AUTHOR_ROLE_OWNER)
def test_anon(self):
self.client.logout()
r = self.client.get(self.get_url, follow=True)
login = reverse('users.login')
self.assertRedirects(r, '%s?to=%s' % (login, self.get_url))
def test_dev_get(self):
eq_(self.client.get(self.get_url).status_code, 200)
def test_dev_post(self):
self.assertRedirects(self.client.post(self.post_url), self.get_url)
def test_viewer_get(self):
self.au.role = amo.AUTHOR_ROLE_VIEWER
self.au.save()
eq_(self.client.get(self.get_url).status_code, 200)
def test_viewer_post(self):
self.au.role = amo.AUTHOR_ROLE_VIEWER
self.au.save()
eq_(self.client.post(self.get_url).status_code, 403)
def test_disabled_post_dev(self):
self.addon.update(status=amo.STATUS_DISABLED)
eq_(self.client.post(self.get_url).status_code, 403)
def test_disabled_post_admin(self):
self.addon.update(status=amo.STATUS_DISABLED)
assert self.client.login(username='admin@mozilla.com',
password='password')
self.assertRedirects(self.client.post(self.post_url), self.get_url)
class TestVersionStats(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
assert self.client.login(username='admin@mozilla.com',
password='password')
def test_counts(self):
addon = Addon.objects.get(id=3615)
version = addon.current_version
user = UserProfile.objects.get(email='admin@mozilla.com')
for _ in range(10):
Review.objects.create(addon=addon, user=user,
version=addon.current_version)
url = reverse('devhub.versions.stats', args=[addon.slug])
r = json.loads(self.client.get(url).content)
exp = {str(version.id):
{'reviews': 10, 'files': 1, 'version': version.version,
'id': version.id}}
self.assertDictEqual(r, exp)
2010-10-08 07:08:41 +04:00
class TestEditPayments(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
self.addon = self.get_addon()
self.addon.the_reason = self.addon.the_future = '...'
self.addon.save()
2010-10-08 07:08:41 +04:00
self.foundation = Charity.objects.create(
id=amo.FOUNDATION_ORG, name='moz', url='$$.moz', paypal='moz.pal')
self.url = reverse('devhub.addons.payments', args=[self.addon.slug])
2010-10-08 07:08:41 +04:00
assert self.client.login(username='del@icio.us', password='password')
2010-10-11 22:24:41 +04:00
self.paypal_mock = mock.Mock()
self.paypal_mock.return_value = (True, None)
paypal.check_paypal_id = self.paypal_mock
2010-10-08 07:08:41 +04:00
def get_addon(self):
return Addon.objects.no_cache().get(id=3615)
def post(self, *args, **kw):
d = dict(*args, **kw)
eq_(self.client.post(self.url, d).status_code, 302)
def check(self, **kw):
addon = self.get_addon()
for k, v in kw.items():
eq_(getattr(addon, k), v)
assert addon.wants_contributions
assert addon.takes_contributions
def test_logging(self):
count = ActivityLog.objects.all().count()
self.post(recipient='dev', suggested_amount=2, paypal_id='greed@dev',
annoying=amo.CONTRIB_AFTER)
eq_(ActivityLog.objects.all().count(), count + 1)
2010-10-08 07:08:41 +04:00
def test_success_dev(self):
self.post(recipient='dev', suggested_amount=2, paypal_id='greed@dev',
annoying=amo.CONTRIB_AFTER)
self.check(paypal_id='greed@dev', suggested_amount=2,
annoying=amo.CONTRIB_AFTER)
def test_success_foundation(self):
self.post(recipient='moz', suggested_amount=2,
2010-10-08 07:08:41 +04:00
annoying=amo.CONTRIB_ROADBLOCK)
self.check(paypal_id='', suggested_amount=2,
2010-10-08 07:08:41 +04:00
charity=self.foundation, annoying=amo.CONTRIB_ROADBLOCK)
def test_success_charity(self):
d = dict(recipient='org', suggested_amount=11.5,
annoying=amo.CONTRIB_PASSIVE)
2010-10-11 22:24:41 +04:00
d.update({'charity-name': 'fligtar fund',
'charity-url': 'http://feed.me',
2010-10-08 07:08:41 +04:00
'charity-paypal': 'greed@org'})
self.post(d)
self.check(paypal_id='', suggested_amount=Decimal('11.50'),
charity=Charity.objects.get(name='fligtar fund'))
def test_dev_paypal_id_length(self):
r = self.client.get(self.url)
2011-02-23 21:25:08 +03:00
doc = pq(r.content)
eq_(int(doc('#id_paypal_id').attr('size')), 50)
2010-10-08 07:08:41 +04:00
def test_dev_paypal_reqd(self):
d = dict(recipient='dev', suggested_amount=2,
annoying=amo.CONTRIB_PASSIVE)
r = self.client.post(self.url, d)
self.assertFormError(r, 'contrib_form', 'paypal_id',
2010-11-30 03:44:25 +03:00
'PayPal ID required to accept contributions.')
2010-10-08 07:08:41 +04:00
def test_bad_paypal_id_dev(self):
2010-10-11 22:24:41 +04:00
self.paypal_mock.return_value = False, 'error'
d = dict(recipient='dev', suggested_amount=2, paypal_id='greed@dev',
annoying=amo.CONTRIB_AFTER)
r = self.client.post(self.url, d)
self.assertFormError(r, 'contrib_form', 'paypal_id', 'error')
def test_bad_paypal_id_charity(self):
self.paypal_mock.return_value = False, 'error'
d = dict(recipient='org', suggested_amount=11.5,
annoying=amo.CONTRIB_PASSIVE)
d.update({'charity-name': 'fligtar fund',
'charity-url': 'http://feed.me',
'charity-paypal': 'greed@org'})
r = self.client.post(self.url, d)
self.assertFormError(r, 'charity_form', 'paypal', 'error')
2010-10-11 22:24:41 +04:00
def test_paypal_timeout(self):
self.paypal_mock.side_effect = socket.timeout()
d = dict(recipient='dev', suggested_amount=2, paypal_id='greed@dev',
annoying=amo.CONTRIB_AFTER)
r = self.client.post(self.url, d)
self.assertFormError(r, 'contrib_form', 'paypal_id',
2010-10-11 22:24:41 +04:00
'Could not validate PayPal id.')
def test_max_suggested_amount(self):
too_much = settings.MAX_CONTRIBUTION + 1
msg = ('Please enter a suggested amount less than $%d.' %
settings.MAX_CONTRIBUTION)
r = self.client.post(self.url, {'suggested_amount': too_much})
self.assertFormError(r, 'contrib_form', 'suggested_amount', msg)
def test_neg_suggested_amount(self):
msg = 'Please enter a suggested amount greater than 0.'
r = self.client.post(self.url, {'suggested_amount': -1})
self.assertFormError(r, 'contrib_form', 'suggested_amount', msg)
2010-10-08 07:08:41 +04:00
def test_charity_details_reqd(self):
d = dict(recipient='org', suggested_amount=11.5,
annoying=amo.CONTRIB_PASSIVE)
r = self.client.post(self.url, d)
self.assertFormError(r, 'charity_form', 'name',
'This field is required.')
eq_(self.get_addon().suggested_amount, None)
def test_switch_charity_to_dev(self):
self.test_success_charity()
self.test_success_dev()
eq_(self.get_addon().charity, None)
eq_(self.get_addon().charity_id, None)
def test_switch_charity_to_foundation(self):
self.test_success_charity()
self.test_success_foundation()
# This will break if we start cleaning up licenses.
old_charity = Charity.objects.get(name='fligtar fund')
assert old_charity.id != self.foundation
def test_switch_foundation_to_charity(self):
self.test_success_foundation()
self.test_success_charity()
moz = Charity.objects.get(id=self.foundation.id)
eq_(moz.name, 'moz')
eq_(moz.url, '$$.moz')
eq_(moz.paypal, 'moz.pal')
def test_contrib_form_initial(self):
eq_(ContribForm.initial(self.addon)['recipient'], 'dev')
self.addon.charity = self.foundation
eq_(ContribForm.initial(self.addon)['recipient'], 'moz')
self.addon.charity_id = amo.FOUNDATION_ORG + 1
eq_(ContribForm.initial(self.addon)['recipient'], 'org')
eq_(ContribForm.initial(self.addon)['annoying'], amo.CONTRIB_PASSIVE)
self.addon.annoying = amo.CONTRIB_AFTER
eq_(ContribForm.initial(self.addon)['annoying'], amo.CONTRIB_AFTER)
2010-10-12 21:51:17 +04:00
def test_enable_thankyou(self):
d = dict(enable_thankyou='on', thankyou_note='woo',
annoying=1, recipient='moz')
r = self.client.post(self.url, d)
eq_(r.status_code, 302)
addon = self.get_addon()
eq_(addon.enable_thankyou, True)
eq_(unicode(addon.thankyou_note), 'woo')
def test_enable_thankyou_unchecked_with_text(self):
d = dict(enable_thankyou='', thankyou_note='woo',
annoying=1, recipient='moz')
r = self.client.post(self.url, d)
eq_(r.status_code, 302)
addon = self.get_addon()
eq_(addon.enable_thankyou, False)
eq_(addon.thankyou_note, None)
def test_contribution_link(self):
self.test_success_foundation()
r = self.client.get(self.url)
doc = pq(r.content)
span = doc('#status-bar').find('span')
eq_(span.length, 1)
assert span.text().startswith('Your contribution page: ')
a = span.find('a')
eq_(a.length, 1)
eq_(a.attr('href'), reverse('addons.about',
args=[self.get_addon().slug]))
eq_(a.text(), url_reverse('addons.about', self.get_addon().slug,
host=settings.SITE_URL))
def test_enable_thankyou_no_text(self):
d = dict(enable_thankyou='on', thankyou_note='',
annoying=1, recipient='moz')
r = self.client.post(self.url, d)
eq_(r.status_code, 302)
addon = self.get_addon()
eq_(addon.enable_thankyou, False)
eq_(addon.thankyou_note, None)
def test_require_public_status_to_edit(self):
# pyquery drops all the attributes on <body> so we just go
# for string search.
assert 'no-edit' not in self.client.get(self.url).content
self.get_addon().update(status=amo.STATUS_LITE)
assert 'no-edit' in self.client.get(self.url).content
def test_no_future(self):
self.get_addon().update(the_future=None)
res = self.client.get(self.url)
box = pq(res.content)('div.notification-box h2')
eq_(len(box), 1)
eq_('completed developer profile' in box.text(), True)
2010-10-12 21:51:17 +04:00
class TestDisablePayments(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
self.addon = Addon.objects.get(id=3615)
self.addon.the_reason = self.addon.the_future = '...'
self.addon.save()
2010-10-12 21:51:17 +04:00
self.addon.update(wants_contributions=True, paypal_id='woohoo')
self.pay_url = reverse('devhub.addons.payments',
args=[self.addon.slug])
2010-10-12 21:51:17 +04:00
self.disable_url = reverse('devhub.addons.payments.disable',
args=[self.addon.slug])
2010-10-12 21:51:17 +04:00
assert self.client.login(username='del@icio.us', password='password')
def test_statusbar_visible(self):
r = self.client.get(self.pay_url)
self.assertContains(r, '<div id="status-bar">')
self.addon.update(wants_contributions=False)
r = self.client.get(self.pay_url)
self.assertNotContains(r, '<div id="status-bar">')
def test_disable(self):
r = self.client.post(self.disable_url)
eq_(r.status_code, 302)
assert(r['Location'].endswith(self.pay_url))
eq_(Addon.uncached.get(id=3615).wants_contributions, False)
2010-10-13 20:14:01 +04:00
class TestPaymentsProfile(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
self.addon = a = self.get_addon()
self.url = reverse('devhub.addons.payments', args=[self.addon.slug])
# Make sure all the payment/profile data is clear.
assert not (a.wants_contributions or a.paypal_id or a.the_reason
or a.the_future or a.takes_contributions)
assert self.client.login(username='del@icio.us', password='password')
self.paypal_mock = mock.Mock()
self.paypal_mock.return_value = (True, None)
paypal.check_paypal_id = self.paypal_mock
def get_addon(self):
return Addon.objects.get(id=3615)
def test_intro_box(self):
# We don't have payments/profile set up, so we see the intro.
doc = pq(self.client.get(self.url).content)
assert doc('.intro')
assert doc('#setup.hidden')
def test_status_bar(self):
# We don't have payments/profile set up, so no status bar.
doc = pq(self.client.get(self.url).content)
assert not doc('#status-bar')
def test_profile_form_exists(self):
doc = pq(self.client.get(self.url).content)
2010-11-30 02:31:53 +03:00
assert doc('#trans-the_reason')
assert doc('#trans-the_future')
def test_profile_form_success(self):
d = dict(recipient='dev', suggested_amount=2, paypal_id='xx@yy',
annoying=amo.CONTRIB_ROADBLOCK, the_reason='xxx',
the_future='yyy')
r = self.client.post(self.url, d)
eq_(r.status_code, 302)
# The profile form is gone, we're accepting contributions.
doc = pq(self.client.get(self.url).content)
assert not doc('.intro')
assert not doc('#setup.hidden')
assert doc('#status-bar')
2010-11-30 02:31:53 +03:00
assert not doc('#trans-the_reason')
assert not doc('#trans-the_future')
addon = self.get_addon()
eq_(unicode(addon.the_reason), 'xxx')
eq_(unicode(addon.the_future), 'yyy')
eq_(addon.wants_contributions, True)
def test_profile_required(self):
def check_page(request):
doc = pq(request.content)
assert not doc('.intro')
assert not doc('#setup.hidden')
assert not doc('#status-bar')
2010-11-30 02:31:53 +03:00
assert doc('#trans-the_reason')
assert doc('#trans-the_future')
d = dict(recipient='dev', suggested_amount=2, paypal_id='xx@yy',
annoying=amo.CONTRIB_ROADBLOCK)
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'profile_form', 'the_reason',
'This field is required.')
self.assertFormError(r, 'profile_form', 'the_future',
'This field is required.')
check_page(r)
eq_(self.get_addon().wants_contributions, False)
d = dict(recipient='dev', suggested_amount=2, paypal_id='xx@yy',
annoying=amo.CONTRIB_ROADBLOCK, the_reason='xxx')
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'profile_form', 'the_future',
'This field is required.')
check_page(r)
eq_(self.get_addon().wants_contributions, False)
2010-11-15 21:56:20 +03:00
class TestDelete(test_utils.TestCase):
fixtures = ('base/apps', 'base/users', 'base/addon_3615',
'base/addon_5579',)
def setUp(self):
self.addon = self.get_addon()
assert self.client.login(username='del@icio.us', password='password')
self.url = reverse('devhub.addons.delete', args=[self.addon.slug])
2010-11-15 21:56:20 +03:00
def get_addon(self):
return Addon.objects.no_cache().get(id=3615)
def test_post_nopw(self):
r = self.client.post(self.url, follow=True)
eq_(pq(r.content)('.notification-box').text(),
'Password was incorrect. Add-on was not deleted.')
def test_post(self):
r = self.client.post(self.url, dict(password='password'), follow=True)
eq_(pq(r.content)('.notification-box').text(), 'Add-on deleted.')
self.assertRaises(Addon.DoesNotExist, self.get_addon)
class TestEdit(test_utils.TestCase):
fixtures = ('base/apps', 'base/users', 'base/addon_3615',
'base/addon_5579', 'base/addon_3615_categories')
2010-10-13 20:14:01 +04:00
def setUp(self):
super(TestEdit, self).setUp()
addon = self.get_addon()
2010-10-13 20:14:01 +04:00
assert self.client.login(username='del@icio.us', password='password')
a = AddonCategory.objects.filter(addon=addon, category__id=22)[0]
a.feature = False
a.save()
AddonCategory.objects.filter(addon=addon,
category__id__in=[23, 24]).delete()
cache.clear()
self.url = reverse('devhub.addons.edit', args=[addon.slug])
2010-11-11 22:33:39 +03:00
self.user = UserProfile.objects.get(pk=55021)
self.tags = ['tag3', 'tag2', 'tag1']
for t in self.tags:
Tag(tag_text=t).save_tag(addon)
2010-11-23 06:03:07 +03:00
self.addon = self.get_addon()
self.old_settings = {
'preview': settings.PREVIEW_THUMBNAIL_PATH,
2011-01-18 13:53:22 +03:00
'icons': settings.ADDON_ICONS_PATH,
}
settings.PREVIEW_THUMBNAIL_PATH = tempfile.mkstemp()[1] + '%s/%d.png'
settings.ADDON_ICONS_PATH = tempfile.mkdtemp()
self.basic_url = self.get_url('basic', True)
ctx = self.client.get(self.basic_url).context['cat_form']
self.cat_initial = initial(ctx.initial_forms[0])
self.preview_upload = reverse('devhub.addons.upload_preview',
args=[self.addon.slug])
self.icon_upload = reverse('devhub.addons.upload_icon',
args=[self.addon.slug])
def tearDown(self):
super(TestEdit, self).tearDown()
settings.PREVIEW_THUMBNAIL_PATH = self.old_settings['preview']
settings.ADDON_ICONS_PATH = self.old_settings['icons']
2010-12-31 04:02:31 +03:00
def formset_new_form(self, *args, **kw):
ctx = self.client.get(self.get_url('media', True)).context
blank = initial(ctx['preview_form'].forms[-1])
blank.update(**kw)
return blank
def formset_media(self, *args, **kw):
kw.setdefault('initial_count', 0)
kw.setdefault('prefix', 'files')
fs = formset(*[a for a in args] + [self.formset_new_form()], **kw)
return dict([(k, '' if v is None else v) for k, v in fs.items()])
def get_addon(self):
return Addon.objects.no_cache().get(id=3615)
2010-10-13 20:14:01 +04:00
def get_url(self, section, edit=False):
args = [self.addon.slug, section]
if edit:
args.append('edit')
return reverse('devhub.addons.section', args=args)
2010-10-13 20:14:01 +04:00
def test_redirect(self):
# /addon/:id => /addon/:id/edit
r = self.client.get('/en-US/developers/addon/3615/', follow=True)
url = reverse('devhub.addons.edit', args=['a3615'])
2010-10-13 20:14:01 +04:00
self.assertRedirects(r, url, 301)
2010-10-11 02:51:45 +04:00
def get_dict(self, **kw):
fs = formset(self.cat_initial, initial_count=1)
result = {'name': 'new name', 'slug': 'test_slug',
'summary': 'new summary',
'tags': ', '.join(self.tags)}
result.update(**kw)
result.update(fs)
return result
def test_edit_basic(self):
old_name = self.addon.name
data = self.get_dict()
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
addon = self.get_addon()
eq_(unicode(addon.name), data['name'])
eq_(addon.name.id, old_name.id)
eq_(unicode(addon.slug), data['slug'])
eq_(unicode(addon.summary), data['summary'])
eq_([unicode(t) for t in addon.tags.all()], sorted(self.tags))
2010-11-11 22:33:39 +03:00
def test_edit_basic_check_description(self):
# Make sure bug 629779 doesn't return.
old_desc = self.addon.description
data = self.get_dict()
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
addon = self.get_addon()
eq_(addon.description, old_desc)
def test_edit_slug_invalid(self):
old_edit = self.get_url('basic', True)
data = self.get_dict(name='', slug='invalid')
r = self.client.post(self.get_url('basic', True), data)
doc = pq(r.content)
eq_(doc('form').attr('action'), old_edit)
def test_edit_slug_valid(self):
old_edit = self.get_url('basic', True)
data = self.get_dict(slug='valid')
r = self.client.post(self.get_url('basic', True), data)
doc = pq(r.content)
assert doc('form').attr('action') != old_edit
def test_edit_summary_escaping(self):
data = self.get_dict()
data['summary'] = '<b>oh my</b>'
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
# Fetch the page so the LinkifiedTranslation gets in cache.
r = self.client.get(reverse('devhub.addons.edit', args=[data['slug']]))
eq_(pq(r.content)('[data-name=summary]').html().strip(),
'<span lang="en-us">&lt;b&gt;oh my&lt;/b&gt;</span>')
# Now make sure we don't have escaped content in the rendered form.
form = AddonFormBasic(instance=self.get_addon(), request=object())
eq_(pq('<body>%s</body>' % form['summary'])('[lang="en-us"]').html(),
'<b>oh my</b>')
def test_edit_basic_as_developer(self):
self.client.login(username='regular@mozilla.com', password='password')
data = self.get_dict()
r = self.client.post(self.get_url('basic', True), data)
# Make sure we get errors when they are just regular users.
eq_(r.status_code, 403)
devuser = UserProfile.objects.get(pk=999)
AddonUser.objects.create(addon=self.get_addon(), user=devuser,
role=amo.AUTHOR_ROLE_DEV)
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
addon = self.get_addon()
eq_(unicode(addon.name), data['name'])
eq_(unicode(addon.slug), data['slug'])
eq_(unicode(addon.summary), data['summary'])
eq_([unicode(t) for t in addon.tags.all()], sorted(self.tags))
def test_edit_basic_name_required(self):
data = self.get_dict(name='', slug='test_addon')
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'name', 'This field is required.')
def test_edit_basic_name_spaces(self):
data = self.get_dict(name=' ', slug='test_addon')
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'name', 'This field is required.')
def test_edit_basic_slugs_unique(self):
Addon.objects.get(id=5579).update(slug='test_slug')
data = self.get_dict()
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'slug', 'This slug is already in use.')
2010-11-11 22:33:39 +03:00
def test_edit_basic_add_tag(self):
count = ActivityLog.objects.all().count()
self.tags.insert(0, 'tag4')
data = self.get_dict()
2010-11-11 22:33:39 +03:00
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
result = pq(r.content)('#addon_tags_edit').eq(0).text()
2010-11-11 22:33:39 +03:00
eq_(result, ', '.join(sorted(self.tags)))
eq_((ActivityLog.objects.for_addons(self.addon)
.get(action=amo.LOG.ADD_TAG.id)).to_string(),
'<a href="/en-US/firefox/tag/tag4">tag4</a> added to '
'<a href="/en-US/firefox/addon/test_slug/">new name</a>.')
2010-11-11 22:33:39 +03:00
eq_(ActivityLog.objects.filter(action=amo.LOG.ADD_TAG.id).count(),
count + 1)
def test_edit_basic_blacklisted_tag(self):
Tag.objects.get_or_create(tag_text='blue', blacklisted=True)
data = self.get_dict(tags='blue')
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
2011-01-13 05:27:13 +03:00
error = 'Invalid tag: blue'
self.assertFormError(r, 'form', 'tags', error)
def test_edit_basic_blacklisted_tags_2(self):
Tag.objects.get_or_create(tag_text='blue', blacklisted=True)
Tag.objects.get_or_create(tag_text='darn', blacklisted=True)
data = self.get_dict(tags='blue, darn, swearword')
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
2011-01-13 05:27:13 +03:00
error = 'Invalid tags: blue, darn'
self.assertFormError(r, 'form', 'tags', error)
def test_edit_basic_blacklisted_tags_3(self):
Tag.objects.get_or_create(tag_text='blue', blacklisted=True)
Tag.objects.get_or_create(tag_text='darn', blacklisted=True)
Tag.objects.get_or_create(tag_text='swearword', blacklisted=True)
data = self.get_dict(tags='blue, darn, swearword')
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
2011-01-13 05:27:13 +03:00
error = 'Invalid tags: blue, darn, swearword'
self.assertFormError(r, 'form', 'tags', error)
2010-11-11 22:33:39 +03:00
def test_edit_basic_remove_tag(self):
self.tags.remove('tag2')
count = ActivityLog.objects.all().count()
data = self.get_dict()
2010-11-11 22:33:39 +03:00
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
result = pq(r.content)('#addon_tags_edit').eq(0).text()
2010-11-11 22:33:39 +03:00
eq_(result, ', '.join(sorted(self.tags)))
2010-11-11 22:33:39 +03:00
eq_(ActivityLog.objects.filter(action=amo.LOG.REMOVE_TAG.id).count(),
count + 1)
def test_edit_basic_minlength_tags(self):
tags = self.tags
tags.append('a' * (amo.MIN_TAG_LENGTH - 1))
data = self.get_dict()
2010-11-11 22:33:39 +03:00
r = self.client.post(self.get_url('basic', True), data)
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'tags',
'All tags must be at least %d characters.' %
amo.MIN_TAG_LENGTH)
def test_edit_basic_max_tags(self):
tags = self.tags
for i in range(amo.MAX_TAGS + 1):
tags.append('test%d' % i)
data = self.get_dict()
2010-11-11 22:33:39 +03:00
r = self.client.post(self.get_url('basic', True), data)
self.assertFormError(r, 'form', 'tags', 'You have %d too many tags.' %
(len(tags) - amo.MAX_TAGS))
def test_edit_tag_empty_after_slug(self):
start = Tag.objects.all().count()
data = self.get_dict(tags='>>')
self.client.post(self.get_url('basic', True), data)
# Check that the tag did not get created.
eq_(start, Tag.objects.all().count())
def test_edit_tag_slugified(self):
data = self.get_dict(tags='<script>alert("foo")</script>')
self.client.post(self.get_url('basic', True), data)
tag = Tag.objects.all().order_by('-pk')[0]
eq_(tag.tag_text, 'scriptalertfooscript')
def test_edit_basic_categories_add(self):
eq_([c.id for c in self.get_addon().all_categories], [22])
self.cat_initial['categories'] = [22, 23]
2011-01-13 01:27:31 +03:00
2011-01-18 13:53:22 +03:00
self.client.post(self.basic_url, self.get_dict())
2011-01-13 01:27:31 +03:00
addon_cats = self.get_addon().categories.values_list('id', flat=True)
eq_(sorted(addon_cats), [22, 23])
def _feature_addon(self, addon_id=3615):
c = CollectionAddon.objects.create(addon_id=addon_id,
collection=Collection.objects.create())
FeaturedCollection.objects.create(collection=c.collection,
application_id=amo.FIREFOX.id)
@mock.patch.object(settings, 'NEW_FEATURES', False)
def test_edit_basic_categories_add_old_creatured(self):
"""Using the old features, categories should be able to be changed."""
self._feature_addon()
self.cat_initial['categories'] = [22, 23]
self.client.post(self.basic_url, self.get_dict())
addon_cats = self.get_addon().categories.values_list('id', flat=True)
# This add-on's categories should change.
eq_(sorted(addon_cats), [22, 23])
2011-07-06 04:54:20 +04:00
@mock.patch.object(settings, 'NEW_FEATURES', True)
def test_edit_basic_categories_add_new_creatured(self):
"""Ensure that categories cannot be changed for creatured add-ons."""
self._feature_addon()
self.cat_initial['categories'] = [22, 23]
r = self.client.post(self.basic_url, self.get_dict())
addon_cats = self.get_addon().categories.values_list('id', flat=True)
eq_(r.context['cat_form'].errors[0]['categories'],
['Categories cannot be changed while your add-on is featured for '
'this application.'])
# This add-on's categories should not change.
eq_(sorted(addon_cats), [22])
@mock.patch.object(settings, 'NEW_FEATURES', True)
def test_edit_basic_categories_add_new_creatured(self):
"""Ensure that other forms are okay when disabling category changes."""
self._feature_addon()
self.cat_initial['categories'] = [22, 23]
data = self.get_dict()
r = self.client.post(self.basic_url, data)
eq_(unicode(self.get_addon().name), data['name'])
@mock.patch.object(settings, 'NEW_FEATURES', False)
def test_edit_basic_categories_no_disclaimer_old(self):
"""With old features enabled, there should never be a disclaimer."""
self._feature_addon()
r = self.client.get(self.basic_url)
doc = pq(r.content)
eq_(doc('#addon-categories-edit div.addon-app-cats').length, 1)
eq_(doc('#addon-categories-edit > p').length, 0)
2011-07-06 04:54:20 +04:00
@mock.patch.object(settings, 'NEW_FEATURES', True)
def test_edit_basic_categories_no_disclaimer_new(self):
"""Ensure that there is a not disclaimer for non-creatured add-ons."""
r = self.client.get(self.basic_url)
doc = pq(r.content)
eq_(doc('#addon-categories-edit div.addon-app-cats').length, 1)
eq_(doc('#addon-categories-edit > p').length, 0)
2011-07-06 04:54:20 +04:00
@mock.patch.object(settings, 'NEW_FEATURES', True)
def test_edit_basic_categories_disclaimer(self):
"""Ensure that there is a disclaimer for creatured add-ons."""
self._feature_addon()
r = self.client.get(self.basic_url)
doc = pq(r.content)
eq_(doc('#addon-categories-edit div.addon-app-cats').length, 0)
eq_(doc('#addon-categories-edit > p').length, 2)
eq_(doc('#addon-categories-edit p.addon-app-cats').text(),
'Firefox: %s' % unicode(Category.objects.get(id=22).name))
def test_edit_basic_categories_addandremove(self):
AddonCategory(addon=self.addon, category_id=23).save()
eq_([c.id for c in self.get_addon().all_categories], [22, 23])
self.cat_initial['categories'] = [22, 24]
2011-01-18 13:53:22 +03:00
self.client.post(self.basic_url, self.get_dict())
2011-01-13 01:27:31 +03:00
addon_cats = self.get_addon().categories.values_list('id', flat=True)
eq_(sorted(addon_cats), [22, 24])
2010-12-18 07:00:32 +03:00
def test_edit_basic_categories_xss(self):
c = Category.objects.get(id=22)
c.name = '<script>alert("test");</script>'
c.save()
self.cat_initial['categories'] = [22, 24]
r = self.client.post(self.basic_url, formset(self.cat_initial,
initial_count=1))
2010-12-18 07:00:32 +03:00
assert '<script>alert' not in r.content
assert '&lt;script&gt;alert' in r.content
def test_edit_basic_categories_remove(self):
c = Category.objects.get(id=23)
AddonCategory(addon=self.addon, category=c).save()
eq_([c.id for c in self.get_addon().all_categories], [22, 23])
self.cat_initial['categories'] = [22]
2011-01-18 13:53:22 +03:00
self.client.post(self.basic_url, self.get_dict())
2011-01-13 01:27:31 +03:00
addon_cats = self.get_addon().categories.values_list('id', flat=True)
eq_(sorted(addon_cats), [22])
def test_edit_basic_categories_required(self):
del self.cat_initial['categories']
r = self.client.post(self.basic_url, formset(self.cat_initial,
initial_count=1))
eq_(r.context['cat_form'].errors[0]['categories'],
['This field is required.'])
def test_edit_basic_categories_max(self):
eq_(amo.MAX_CATEGORIES, 2)
self.cat_initial['categories'] = [22, 23, 24]
r = self.client.post(self.basic_url, formset(self.cat_initial,
initial_count=1))
eq_(r.context['cat_form'].errors[0]['categories'],
['You can have only 2 categories.'])
def test_edit_basic_categories_other_failure(self):
Category.objects.get(id=22).update(misc=True)
self.cat_initial['categories'] = [22, 23]
r = self.client.post(self.basic_url, formset(self.cat_initial,
initial_count=1))
eq_(r.context['cat_form'].errors[0]['categories'],
['The miscellaneous category cannot be combined with additional '
'categories.'])
def test_edit_basic_categories_nonexistent(self):
self.cat_initial['categories'] = [100]
r = self.client.post(self.basic_url, formset(self.cat_initial,
initial_count=1))
eq_(r.context['cat_form'].errors[0]['categories'],
['Select a valid choice. 100 is not one of the available '
'choices.'])
2010-11-11 22:33:39 +03:00
def test_edit_basic_name_not_empty(self):
data = self.get_dict(name='', slug=self.addon.slug,
summary=self.addon.summary)
r = self.client.post(self.get_url('basic', True), data)
self.assertFormError(r, 'form', 'name', 'This field is required.')
2010-11-09 07:11:07 +03:00
def test_edit_basic_name_max_length(self):
data = self.get_dict(name='xx' * 70, slug=self.addon.slug,
summary=self.addon.summary)
2010-11-09 07:11:07 +03:00
r = self.client.post(self.get_url('basic', True), data)
self.assertFormError(r, 'form', 'name',
2010-11-18 00:41:34 +03:00
'Ensure this value has at most 50 '
2010-11-09 07:11:07 +03:00
'characters (it has 140).')
def test_edit_basic_summary_max_length(self):
data = self.get_dict(name=self.addon.name, slug=self.addon.slug,
summary='x' * 251)
2010-11-09 07:11:07 +03:00
r = self.client.post(self.get_url('basic', True), data)
self.assertFormError(r, 'form', 'summary',
'Ensure this value has at most 250 '
'characters (it has 251).')
2010-10-15 08:12:56 +04:00
def test_edit_details(self):
data = dict(description='New description with <em>html</em>!',
default_locale='en-US',
2010-10-15 08:12:56 +04:00
homepage='http://twitter.com/fligtarsmom')
r = self.client.post(self.get_url('details', True), data)
eq_(r.context['form'].errors, {})
2010-10-15 08:12:56 +04:00
addon = self.get_addon()
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_details_xss(self):
"""
Let's try to put xss in our description, and safe html, and verify
that we are playing safe.
"""
self.addon.description = ("This\n<b>IS</b>"
"<script>alert('awesome')</script>")
self.addon.save()
r = self.client.get(reverse('devhub.addons.edit',
args=[self.addon.slug]))
doc = pq(r.content)
eq_(doc('#edit-addon-details span[lang]').html(),
"This<br/><b>IS</b>&lt;script&gt;alert('awesome')"
'&lt;/script&gt;')
def test_edit_basic_homepage_optional(self):
data = dict(description='New description with <em>html</em>!',
default_locale='en-US', homepage='')
r = self.client.post(self.get_url('details', True), data)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_default_locale_required_trans(self):
# name, summary, and description are required in the new locale.
description, homepage = map(unicode, [self.addon.description,
self.addon.homepage])
# TODO: description should get fixed up with the form.
fields = ['description', 'name', 'summary']
2010-12-10 02:55:30 +03:00
error = ('Before changing your default locale you must have a name, '
'summary, and description in that locale. '
'You are missing %s.')
missing = lambda f: error % ', '.join(map(repr, f))
d = dict(description=description, homepage=homepage,
default_locale='fr')
r = self.client.post(self.get_url('details', True), d)
self.assertFormError(r, 'form', None, missing(fields))
# Now we have a name.
self.addon.name = {'fr': 'fr name'}
2010-12-27 19:20:55 +03:00
self.addon.save()
fields.remove('name')
r = self.client.post(self.get_url('details', True), d)
self.assertFormError(r, 'form', None, missing(fields))
# Now we have a summary.
self.addon.summary = {'fr': 'fr summary'}
2010-12-27 19:20:55 +03:00
self.addon.save()
fields.remove('summary')
r = self.client.post(self.get_url('details', True), d)
self.assertFormError(r, 'form', None, missing(fields))
# Now we're sending an fr description with the form.
d['description_fr'] = 'fr description'
r = self.client.post(self.get_url('details', True), d)
eq_(r.context['form'].errors, {})
def test_edit_default_locale_frontend_error(self):
d = dict(description='xx', homepage='yy', default_locale='fr')
r = self.client.post(self.get_url('details', True), d)
2010-12-10 02:55:30 +03:00
self.assertContains(r, 'Before changing your default locale you must')
2010-10-15 08:12:56 +04:00
def test_edit_details_locale(self):
addon = self.get_addon()
addon.update(default_locale='en-US')
r = self.client.get(self.get_url('details', False))
2010-10-15 08:12:56 +04:00
eq_(pq(r.content)('.addon_edit_locale').eq(0).text(), "English (US)")
2010-10-15 08:12:56 +04:00
def test_edit_details_restricted_tags(self):
addon = self.get_addon()
tag = Tag.objects.create(tag_text='restartless', restricted=True)
AddonTag.objects.create(tag=tag, addon=addon)
res = self.client.get(self.get_url('basic', True))
divs = pq(res.content)('#addon_tags_edit .edit-addon-details')
eq_(len(divs), 2)
assert 'restartless' in divs.eq(1).text()
def test_edit_support(self):
data = dict(support_email='sjobs@apple.com',
support_url='http://apple.com/')
r = self.client.post(self.get_url('support', True), data)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_support_getsatisfaction(self):
urls = [("http://getsatisfaction.com/abc/products/def", 'abcdef'),
("http://getsatisfaction.com/abc/", 'abc'), # No company
("http://google.com", None)] # Delete GS
for (url, val) in urls:
data = dict(support_email='abc@def.com',
support_url=url)
r = self.client.post(self.get_url('support', True), data)
eq_(r.context['form'].errors, {})
result = pq(r.content)('.addon_edit_gs').eq(0).text()
doc = pq(r.content)
result = doc('.addon_edit_gs').eq(0).text()
result = re.sub('\W', '', result) if result else None
eq_(result, val)
def test_edit_support_optional_url(self):
data = dict(support_email='sjobs@apple.com',
support_url='')
r = self.client.post(self.get_url('support', True), data)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_support_optional_email(self):
data = dict(support_email='',
support_url='http://apple.com/')
r = self.client.post(self.get_url('support', True), data)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_media_defaulticon(self):
data = dict(icon_type='')
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
2010-12-31 04:02:31 +03:00
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
assert addon.get_icon_url(64).endswith('icons/default-64.png')
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_media_preuploadedicon(self):
data = dict(icon_type='icon/appearance')
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
2010-12-31 04:02:31 +03:00
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
assert addon.get_icon_url(64).endswith('icons/appearance-64.png')
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_media_uploadedicon(self):
img = get_image_path('mozilla.png')
src_image = open(img, 'rb')
data = dict(upload_image=src_image)
response = self.client.post(self.icon_upload, data)
response_json = json.loads(response.content)
addon = self.get_addon()
# Now, save the form so it gets moved properly.
data = dict(icon_type='image/png',
icon_upload_hash=response_json['upload_hash'])
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
2010-12-31 04:02:31 +03:00
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
url = addon.get_icon_url(64)
assert ('addon_icon/%s' % addon.id) in url, (
"Unexpected path: %r" % url)
eq_(data['icon_type'], 'image/png')
# Check that it was actually uploaded
dirname = os.path.join(settings.ADDON_ICONS_PATH,
'%s' % (addon.id / 1000))
dest = os.path.join(dirname, '%s-32.png' % addon.id)
assert os.path.exists(dest)
eq_(Image.open(dest).size, (32, 12))
2010-12-30 20:54:28 +03:00
def test_edit_media_icon_log(self):
self.test_edit_media_uploadedicon()
log = ActivityLog.objects.all()
eq_(log.count(), 1)
eq_(log[0].action, amo.LOG.CHANGE_ICON.id)
def test_edit_media_uploadedicon_noresize(self):
img = "%s/img/notifications/error.png" % settings.MEDIA_ROOT
src_image = open(img, 'rb')
data = dict(upload_image=src_image)
response = self.client.post(self.icon_upload, data)
response_json = json.loads(response.content)
addon = self.get_addon()
# Now, save the form so it gets moved properly.
data = dict(icon_type='image/png',
icon_upload_hash=response_json['upload_hash'])
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
2010-12-31 04:02:31 +03:00
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
url = addon.get_icon_url(64)
assert ('addon_icon/%s' % addon.id) in url, (
"Unexpected path: %r" % url)
eq_(data['icon_type'], 'image/png')
# Check that it was actually uploaded
dirname = os.path.join(settings.ADDON_ICONS_PATH,
'%s' % (addon.id / 1000))
dest = os.path.join(dirname, '%s-64.png' % addon.id)
assert os.path.exists(dest)
eq_(Image.open(dest).size, (48, 48))
def test_edit_media_uploadedicon_wrongtype(self):
2010-12-10 04:38:28 +03:00
img = "%s/js/zamboni/devhub.js" % settings.MEDIA_ROOT
src_image = open(img, 'rb')
data = {'upload_image': src_image}
res = self.client.post(self.preview_upload, data)
response_json = json.loads(res.content)
eq_(response_json['errors'][0], u'Icons must be either PNG or JPG.')
def setup_image_status(self):
2010-12-30 20:54:28 +03:00
addon = self.get_addon()
self.icon_dest = os.path.join(addon.get_icon_dir(),
'%s-32.png' % addon.id)
os.makedirs(os.path.dirname(self.icon_dest))
open(self.icon_dest, 'w')
self.preview = addon.previews.create()
self.preview.save()
os.makedirs(os.path.dirname(self.preview.thumbnail_path))
open(self.preview.thumbnail_path, 'w')
self.url = reverse('devhub.ajax.image.status', args=[addon.slug])
def test_image_status_no_choice(self):
addon = self.get_addon()
addon.update(icon_type='')
url = reverse('devhub.ajax.image.status', args=[addon.slug])
result = json.loads(self.client.get(url).content)
assert result['icons']
def test_image_status_works(self):
self.setup_image_status()
result = json.loads(self.client.get(self.url).content)
assert result['icons']
def test_image_status_fails(self):
self.setup_image_status()
2010-12-30 20:54:28 +03:00
os.remove(self.icon_dest)
result = json.loads(self.client.get(self.url).content)
assert not result['icons']
def test_preview_status_works(self):
self.setup_image_status()
result = json.loads(self.client.get(self.url).content)
assert result['previews']
# No previews means that all the images are done.
self.addon.previews.all().delete()
result = json.loads(self.client.get(self.url).content)
assert result['previews']
def test_preview_status_fails(self):
self.setup_image_status()
os.remove(self.preview.thumbnail_path)
result = json.loads(self.client.get(self.url).content)
assert not result['previews']
2010-12-30 20:54:28 +03:00
def test_image_status_persona(self):
self.setup_image_status()
os.remove(self.icon_dest)
self.get_addon().update(type=amo.ADDON_PERSONA)
result = json.loads(self.client.get(self.url).content)
assert result['icons']
def test_image_status_default(self):
self.setup_image_status()
os.remove(self.icon_dest)
self.get_addon().update(icon_type='icon/photos')
result = json.loads(self.client.get(self.url).content)
assert result['icons']
2010-12-10 22:52:26 +03:00
def test_icon_animated(self):
filehandle = open(get_image_path('animated.png'), 'rb')
data = {'upload_image': filehandle}
res = self.client.post(self.preview_upload, data)
response_json = json.loads(res.content)
eq_(response_json['errors'][0], u'Icons cannot be animated.')
2010-12-10 22:52:26 +03:00
2010-12-31 04:02:31 +03:00
def preview_add(self, amount=1):
img = get_image_path('mozilla.png')
2010-12-31 04:02:31 +03:00
src_image = open(img, 'rb')
data = dict(upload_image=src_image)
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
url = self.preview_upload
2010-12-31 04:02:31 +03:00
r = self.client.post(url, data_formset)
details = json.loads(r.content)
upload_hash = details['upload_hash']
# Create and post with the formset.
fields = []
for i in range(amount):
fields.append(self.formset_new_form(caption='hi',
2011-01-11 22:53:03 +03:00
upload_hash=upload_hash,
position=i))
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(*fields)
self.get_url('media', True)
r = self.client.post(self.get_url('media', True), data_formset)
def test_edit_media_preview_add(self):
self.preview_add()
eq_(str(self.get_addon().previews.all()[0].caption), 'hi')
def test_edit_media_preview_edit(self):
self.preview_add()
preview = self.get_addon().previews.all()[0]
edited = {'caption': 'bye',
'upload_hash': '',
'id': preview.id,
2011-01-11 22:53:03 +03:00
'position': preview.position,
2010-12-31 04:02:31 +03:00
'file_upload': None}
data_formset = self.formset_media(edited, initial_count=1)
self.client.post(self.get_url('media', True), data_formset)
eq_(str(self.get_addon().previews.all()[0].caption), 'bye')
eq_(len(self.get_addon().previews.all()), 1)
2011-01-11 22:53:03 +03:00
def test_edit_media_preview_reorder(self):
self.preview_add(3)
previews = self.get_addon().previews.all()
base = dict(upload_hash='', file_upload=None)
# Three preview forms were generated; mix them up here.
a = dict(caption="first", position=1, id=previews[2].id)
b = dict(caption="second", position=2, id=previews[0].id)
c = dict(caption="third", position=3, id=previews[1].id)
a.update(base)
b.update(base)
c.update(base)
# Add them in backwards ("third", "second", "first")
data_formset = self.formset_media(c, b, a, initial_count=3)
eq_(data_formset['files-0-caption'], 'third')
eq_(data_formset['files-1-caption'], 'second')
eq_(data_formset['files-2-caption'], 'first')
self.client.post(self.get_url('media', True), data_formset)
# They should come out "first", "second", "third"
eq_(self.get_addon().previews.all()[0].caption, 'first')
eq_(self.get_addon().previews.all()[1].caption, 'second')
eq_(self.get_addon().previews.all()[2].caption, 'third')
2011-01-06 02:29:35 +03:00
def test_edit_media_preview_delete(self):
self.preview_add()
preview = self.get_addon().previews.get()
edited = {'DELETE': 'checked',
'upload_hash': '',
'id': preview.id,
2011-01-11 22:53:03 +03:00
'position': 0,
2011-01-06 02:29:35 +03:00
'file_upload': None}
data_formset = self.formset_media(edited, initial_count=1)
self.client.post(self.get_url('media', True), data_formset)
eq_(len(self.get_addon().previews.all()), 0)
2010-12-31 04:02:31 +03:00
def test_edit_media_preview_add_another(self):
self.preview_add()
self.preview_add()
eq_(len(self.get_addon().previews.all()), 2)
def test_edit_media_preview_add_two(self):
self.preview_add(2)
eq_(len(self.get_addon().previews.all()), 2)
def test_log(self):
data = {'developer_comments': 'This is a test'}
o = ActivityLog.objects
eq_(o.count(), 0)
r = self.client.post(self.get_url('technical', True), data)
eq_(r.context['form'].errors, {})
eq_(o.filter(action=amo.LOG.EDIT_PROPERTIES.id).count(), 1)
def test_technical_on(self):
# Turn everything on
data = dict(developer_comments='Test comment!',
binary='on',
external_software='on',
site_specific='on',
view_source='on')
r = self.client.post(self.get_url('technical', True), data)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
for k in data:
if k == 'developer_comments':
eq_(unicode(getattr(addon, k)), unicode(data[k]))
else:
eq_(getattr(addon, k), True if data[k] == 'on' else False)
# Andddd offf
data = dict(developer_comments='Test comment!')
r = self.client.post(self.get_url('technical', True), data)
addon = self.get_addon()
eq_(addon.binary, False)
eq_(addon.external_software, False)
eq_(addon.site_specific, False)
eq_(addon.view_source, False)
def test_technical_devcomment_notrequired(self):
data = dict(developer_comments='',
binary='on',
external_software='on',
site_specific='on',
view_source='on')
r = self.client.post(self.get_url('technical', True), data)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
for k in data:
if k == 'developer_comments':
eq_(unicode(getattr(addon, k)), unicode(data[k]))
else:
eq_(getattr(addon, k), True if data[k] == 'on' else False)
def test_text_not_none_when_has_flags(self):
addon = self.get_addon()
r = self.client.get(reverse('devhub.addons.edit',
kwargs=dict(addon_id=addon.slug)))
doc = pq(r.content)
eq_(doc('#addon-flags').text(), 'This is a site-specific add-on.')
def test_text_none_when_no_flags(self):
addon = self.get_addon()
addon.update(external_software=False, site_specific=False, binary=False)
r = self.client.get(reverse('devhub.addons.edit',
kwargs=dict(addon_id=addon.slug)))
doc = pq(r.content)
eq_(doc('#addon-flags').text(), 'None')
def test_auto_repackage_not_shown(self):
f = self.addon.current_version.all_files[0]
f.jetpack_version = None
f.save()
r = self.client.get(self.get_url('technical'))
self.assertNotContains(r, 'Upgrade SDK?')
def test_auto_repackage_shown(self):
f = self.addon.current_version.all_files[0]
f.jetpack_version = '1.0'
f.save()
r = self.client.get(self.get_url('technical'))
self.assertContains(r, 'Upgrade SDK?')
def test_nav_links(self):
url = reverse('devhub.addons.edit', args=['a3615'])
activity_url = reverse('devhub.feed', args=['a3615'])
r = self.client.get(url)
doc = pq(r.content)
eq_(doc('#edit-addon-nav ul:last').find('li a').eq(1).attr('href'),
activity_url)
def get_l10n_urls(self):
paths = ('devhub.addons.edit', 'devhub.addons.profile',
'devhub.addons.payments', 'devhub.addons.owner')
return [reverse(p, args=['a3615']) for p in paths]
def test_l10n(self):
2010-12-02 20:23:37 +03:00
Addon.objects.get(id=3615).update(default_locale='en-US')
for url in self.get_l10n_urls():
r = self.client.get(url)
eq_(pq(r.content)('#l10n-menu').attr('data-default'), 'en-us')
def test_l10n_not_us(self):
2010-12-02 20:23:37 +03:00
Addon.objects.get(id=3615).update(default_locale='fr')
for url in self.get_l10n_urls():
r = self.client.get(url)
eq_(pq(r.content)('#l10n-menu').attr('data-default'), 'fr')
def test_l10n_not_us_id_url(self):
2010-12-02 20:23:37 +03:00
Addon.objects.get(id=3615).update(default_locale='fr')
for url in self.get_l10n_urls():
url = '/id' + url[6:]
r = self.client.get(url)
eq_(pq(r.content)('#l10n-menu').attr('data-default'), 'fr')
2010-10-11 02:51:45 +04:00
class TestActivityFeed(test_utils.TestCase):
fixtures = ('base/apps', 'base/users', 'base/addon_3615')
def setUp(self):
super(TestActivityFeed, self).setUp()
assert self.client.login(username='del@icio.us', password='password')
def test_feed_for_all(self):
r = self.client.get(reverse('devhub.feed_all'))
eq_(r.status_code, 200)
doc = pq(r.content)
eq_(doc('header h2').text(),
'Recent Activity for My Add-ons')
eq_(doc('.breadcrumbs li:eq(2)').text(),
'Recent Activity')
def test_feed_for_addon(self):
addon = Addon.objects.no_cache().get(id=3615)
r = self.client.get(reverse('devhub.feed', args=[addon.slug]))
eq_(r.status_code, 200)
doc = pq(r.content)
eq_(doc('header h2').text(),
'Recent Activity for %s' % addon.name)
eq_(doc('.breadcrumbs li:eq(3)').text(),
addon.slug)
def test_feed_disabled(self):
addon = Addon.objects.no_cache().get(id=3615)
addon.update(status=amo.STATUS_DISABLED)
r = self.client.get(reverse('devhub.feed', args=[addon.slug]))
eq_(r.status_code, 200)
def test_feed_disabled_anon(self):
self.client.logout()
addon = Addon.objects.no_cache().get(id=3615)
r = self.client.get(reverse('devhub.feed', args=[addon.slug]))
eq_(r.status_code, 302)
2011-06-30 22:17:20 +04:00
def add_comment(self):
addon = Addon.objects.get(id=3615)
amo.set_user(UserProfile.objects.get(email='del@icio.us'))
amo.log(amo.LOG.COMMENT_VERSION, addon, addon.versions.all()[0])
return addon
def test_feed_hidden(self):
addon = self.add_comment()
res = self.client.get(reverse('devhub.feed', args=[addon.slug]))
doc = pq(res.content)
eq_(len(doc('#recent-activity p')), 1)
def test_addons_hidden(self):
self.add_comment()
res = self.client.get(reverse('devhub.addons'))
doc = pq(res.content)
eq_(len(doc('#dashboard-sidebar div.recent-activity li.item')), 0)
class TestProfileBase(test_utils.TestCase):
2010-10-11 02:51:45 +04:00
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
self.url = reverse('devhub.addons.profile', args=['a3615'])
2010-10-11 02:51:45 +04:00
assert self.client.login(username='del@icio.us', password='password')
self.addon = Addon.objects.get(id=3615)
self.version = self.addon.current_version
def get_addon(self):
return Addon.objects.no_cache().get(id=self.addon.id)
def enable_addon_contributions(self):
self.addon.wants_contributions = True
self.addon.paypal_id = 'somebody'
self.addon.save()
def post(self, *args, **kw):
d = dict(*args, **kw)
eq_(self.client.post(self.url, d).status_code, 302)
def check(self, **kw):
addon = self.get_addon()
for k, v in kw.items():
if k in ('the_reason', 'the_future'):
eq_(getattr(getattr(addon, k), 'localized_string'), unicode(v))
else:
eq_(getattr(addon, k), v)
class TestProfileStatusBar(TestProfileBase):
def setUp(self):
super(TestProfileStatusBar, self).setUp()
self.remove_url = reverse('devhub.addons.profile.remove',
args=[self.addon.slug])
def test_no_status_bar(self):
self.addon.the_reason = self.addon.the_future = None
self.addon.save()
assert not pq(self.client.get(self.url).content)('#status-bar')
def test_status_bar_no_contrib(self):
self.addon.the_reason = self.addon.the_future = '...'
self.addon.wants_contributions = False
self.addon.save()
doc = pq(self.client.get(self.url).content)
assert doc('#status-bar')
eq_(doc('#status-bar button').text(), 'Remove Profile')
def test_status_bar_with_contrib(self):
self.addon.the_reason = self.addon.the_future = '...'
self.addon.wants_contributions = True
self.addon.paypal_id = 'xxx'
self.addon.save()
doc = pq(self.client.get(self.url).content)
assert doc('#status-bar')
eq_(doc('#status-bar button').text(), 'Remove Both')
def test_remove_profile(self):
self.addon.the_reason = self.addon.the_future = '...'
self.addon.save()
self.client.post(self.remove_url)
addon = self.get_addon()
eq_(addon.the_reason, None)
eq_(addon.the_future, None)
eq_(addon.takes_contributions, False)
eq_(addon.wants_contributions, False)
def test_remove_profile_without_content(self):
# See bug 624852
self.addon.the_reason = self.addon.the_future = None
self.addon.save()
self.client.post(self.remove_url)
addon = self.get_addon()
eq_(addon.the_reason, None)
eq_(addon.the_future, None)
def test_remove_both(self):
self.addon.the_reason = self.addon.the_future = '...'
self.addon.wants_contributions = True
self.addon.paypal_id = 'xxx'
self.addon.save()
self.client.post(self.remove_url)
addon = self.get_addon()
eq_(addon.the_reason, None)
eq_(addon.the_future, None)
eq_(addon.takes_contributions, False)
eq_(addon.wants_contributions, False)
class TestProfile(TestProfileBase):
2010-10-11 02:51:45 +04:00
def test_without_contributions_labels(self):
r = self.client.get(self.url)
doc = pq(r.content)
2010-11-30 02:31:53 +03:00
eq_(doc('label[for=the_reason] .optional').length, 1)
eq_(doc('label[for=the_future] .optional').length, 1)
2010-10-11 02:51:45 +04:00
def test_without_contributions_fields_optional(self):
self.post(the_reason='', the_future='')
self.check(the_reason='', the_future='')
self.post(the_reason='to be cool', the_future='')
self.check(the_reason='to be cool', the_future='')
self.post(the_reason='', the_future='hot stuff')
self.check(the_reason='', the_future='hot stuff')
self.post(the_reason='to be hot', the_future='cold stuff')
self.check(the_reason='to be hot', the_future='cold stuff')
def test_with_contributions_labels(self):
self.enable_addon_contributions()
r = self.client.get(self.url)
doc = pq(r.content)
2010-11-30 02:31:53 +03:00
assert doc('label[for=the_reason] .req').length, \
2010-10-11 02:51:45 +04:00
'the_reason field should be required.'
2010-11-30 02:31:53 +03:00
assert doc('label[for=the_future] .req').length, \
2010-10-11 02:51:45 +04:00
'the_future field should be required.'
def test_log(self):
self.enable_addon_contributions()
d = dict(the_reason='because', the_future='i can')
o = ActivityLog.objects
eq_(o.count(), 0)
2010-11-06 00:51:28 +03:00
self.client.post(self.url, d)
eq_(o.filter(action=amo.LOG.EDIT_PROPERTIES.id).count(), 1)
2010-10-11 02:51:45 +04:00
def test_with_contributions_fields_required(self):
self.enable_addon_contributions()
d = dict(the_reason='', the_future='')
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'profile_form', 'the_reason',
'This field is required.')
self.assertFormError(r, 'profile_form', 'the_future',
'This field is required.')
d = dict(the_reason='to be cool', the_future='')
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'profile_form', 'the_future',
'This field is required.')
d = dict(the_reason='', the_future='hot stuff')
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'profile_form', 'the_reason',
'This field is required.')
self.post(the_reason='to be hot', the_future='cold stuff')
self.check(the_reason='to be hot', the_future='cold stuff')
2010-11-17 05:15:54 +03:00
class TestSubmitBase(test_utils.TestCase):
fixtures = ['base/addon_3615', 'base/addon_5579', 'base/users']
def setUp(self):
assert self.client.login(username='del@icio.us', password='password')
def get_addon(self):
return Addon.objects.no_cache().get(pk=3615)
def get_version(self):
return self.get_addon().versions.get()
2010-11-18 02:28:49 +03:00
def get_step(self):
return SubmitStep.objects.get(addon=self.get_addon())
2010-11-17 05:15:54 +03:00
class TestSubmitStep1(TestSubmitBase):
def test_step1_submit(self):
response = self.client.get(reverse('devhub.submit.1'))
eq_(response.status_code, 200)
doc = pq(response.content)
links = doc('#agreement-container a')
assert len(links)
for ln in links:
href = ln.attrib['href']
assert not href.startswith('%'), (
"Looks like link %r to %r is still a placeholder" %
(href, ln.text))
2011-01-18 13:53:22 +03:00
2010-11-17 07:32:40 +03:00
class TestSubmitStep2(test_utils.TestCase):
2010-11-23 04:45:12 +03:00
# More tests in TestCreateAddon.
2010-11-17 07:32:40 +03:00
fixtures = ['base/users']
def setUp(self):
self.client.login(username='regular@mozilla.com', password='password')
def test_step_2_with_cookie(self):
r = self.client.post(reverse('devhub.submit.1'))
self.assertRedirects(r, reverse('devhub.submit.2'))
r = self.client.get(reverse('devhub.submit.2'))
2010-11-17 07:32:40 +03:00
eq_(r.status_code, 200)
def test_step_2_no_cookie(self):
# We require a cookie that gets set in step 1.
r = self.client.get(reverse('devhub.submit.2'), follow=True)
self.assertRedirects(r, reverse('devhub.submit.1'))
2010-11-17 07:32:40 +03:00
class TestSubmitStep3(test_utils.TestCase):
fixtures = ['base/addon_3615', 'base/addon_3615_categories',
'base/addon_5579', 'base/users']
2010-11-17 07:32:40 +03:00
def setUp(self):
super(TestSubmitStep3, self).setUp()
self.addon = self.get_addon()
self.url = reverse('devhub.submit.3', args=['a3615'])
2010-11-17 07:32:40 +03:00
assert self.client.login(username='del@icio.us', password='password')
SubmitStep.objects.create(addon_id=3615, step=3)
cron.build_reverse_name_lookup()
2010-11-23 06:03:07 +03:00
AddonCategory.objects.filter(addon=self.get_addon(),
category=Category.objects.get(id=23)).delete()
AddonCategory.objects.filter(addon=self.get_addon(),
category=Category.objects.get(id=24)).delete()
ctx = self.client.get(self.url).context['cat_form']
self.cat_initial = initial(ctx.initial_forms[0])
def get_addon(self):
return Addon.objects.no_cache().get(id=3615)
def get_dict(self, **kw):
cat_initial = kw.pop('cat_initial', self.cat_initial)
fs = formset(cat_initial, initial_count=1)
result = {'name': 'Test name', 'slug': 'testname',
'description': 'desc', 'summary': 'Hello!'}
result.update(**kw)
result.update(fs)
return result
def test_submit_success(self):
2010-11-17 07:32:40 +03:00
r = self.client.get(self.url)
eq_(r.status_code, 200)
# Post and be redirected.
d = self.get_dict()
2010-11-17 07:32:40 +03:00
r = self.client.post(self.url, d)
eq_(r.status_code, 302)
eq_(SubmitStep.objects.get(addon=3615).step, 4)
addon = self.get_addon()
eq_(addon.name, 'Test name')
eq_(addon.slug, 'testname')
eq_(addon.description, 'desc')
eq_(addon.summary, 'Hello!')
# Test add-on log activity.
log_items = ActivityLog.objects.for_addons(addon)
assert not log_items.filter(action=amo.LOG.EDIT_DESCRIPTIONS.id), \
"Creating a description needn't be logged."
def test_submit_name_unique(self):
# Make sure name is unique.
r = self.client.post(self.url, self.get_dict(name='Cooliris'))
error = 'This add-on name is already in use. Please choose another.'
self.assertFormError(r, 'form', 'name', error)
def test_submit_name_unique_strip(self):
# Make sure we can't sneak in a name by adding a space or two.
r = self.client.post(self.url, self.get_dict(name=' Cooliris '))
error = 'This add-on name is already in use. Please choose another.'
self.assertFormError(r, 'form', 'name', error)
def test_submit_name_unique_case(self):
# Make sure unique names aren't case sensitive.
r = self.client.post(self.url, self.get_dict(name='cooliris'))
error = 'This add-on name is already in use. Please choose another.'
self.assertFormError(r, 'form', 'name', error)
2010-11-17 07:32:40 +03:00
def test_submit_name_required(self):
# Make sure name is required.
r = self.client.post(self.url, self.get_dict(name=''))
2010-11-17 07:32:40 +03:00
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'name', 'This field is required.')
def test_submit_name_length(self):
# Make sure the name isn't too long.
d = self.get_dict(name='a' * 51)
r = self.client.post(self.url, d)
2010-11-17 07:32:40 +03:00
eq_(r.status_code, 200)
error = 'Ensure this value has at most 50 characters (it has 51).'
self.assertFormError(r, 'form', 'name', error)
def test_submit_slug_invalid(self):
# Submit an invalid slug.
d = self.get_dict(slug='slug!!! aksl23%%')
2010-11-17 07:32:40 +03:00
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'slug', "Enter a valid 'slug' " +
"consisting of letters, numbers, underscores or hyphens.")
def test_submit_slug_required(self):
# Make sure the slug is required.
r = self.client.post(self.url, self.get_dict(slug=''))
2010-11-17 07:32:40 +03:00
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'slug', 'This field is required.')
def test_submit_summary_required(self):
# Make sure summary is required.
r = self.client.post(self.url, self.get_dict(summary=''))
2010-11-17 07:32:40 +03:00
eq_(r.status_code, 200)
self.assertFormError(r, 'form', 'summary', 'This field is required.')
def test_submit_summary_length(self):
# Summary is too long.
r = self.client.post(self.url, self.get_dict(summary='a' * 251))
2010-11-17 07:32:40 +03:00
eq_(r.status_code, 200)
error = 'Ensure this value has at most 250 characters (it has 251).'
self.assertFormError(r, 'form', 'summary', error)
def test_submit_categories_required(self):
del self.cat_initial['categories']
r = self.client.post(self.url,
self.get_dict(cat_initial=self.cat_initial))
eq_(r.context['cat_form'].errors[0]['categories'],
['This field is required.'])
def test_submit_categories_max(self):
eq_(amo.MAX_CATEGORIES, 2)
self.cat_initial['categories'] = [22, 23, 24]
r = self.client.post(self.url,
self.get_dict(cat_initial=self.cat_initial))
eq_(r.context['cat_form'].errors[0]['categories'],
['You can have only 2 categories.'])
def test_submit_categories_add(self):
eq_([c.id for c in self.get_addon().all_categories], [22])
self.cat_initial['categories'] = [22, 23]
2011-01-13 01:27:31 +03:00
self.client.post(self.url, self.get_dict())
addon_cats = self.get_addon().categories.values_list('id', flat=True)
eq_(sorted(addon_cats), [22, 23])
def test_submit_categories_addandremove(self):
AddonCategory(addon=self.addon, category_id=23).save()
eq_([c.id for c in self.get_addon().all_categories], [22, 23])
self.cat_initial['categories'] = [22, 24]
self.client.post(self.url, self.get_dict(cat_initial=self.cat_initial))
category_ids_new = [c.id for c in self.get_addon().all_categories]
eq_(category_ids_new, [22, 24])
def test_submit_categories_remove(self):
c = Category.objects.get(id=23)
AddonCategory(addon=self.addon, category=c).save()
eq_([c.id for c in self.get_addon().all_categories], [22, 23])
self.cat_initial['categories'] = [22]
self.client.post(self.url, self.get_dict(cat_initial=self.cat_initial))
category_ids_new = [c.id for c in self.get_addon().all_categories]
eq_(category_ids_new, [22])
def test_check_version(self):
addon = Addon.objects.get(pk=3615)
r = self.client.get(self.url)
doc = pq(r.content)
version = doc("#current_version").val()
eq_(version, addon.current_version.version)
2010-11-19 22:32:13 +03:00
class TestSubmitStep4(TestSubmitBase):
def setUp(self):
2010-12-30 20:54:28 +03:00
self.old_addon_icon_url = settings.ADDON_ICON_URL
url_string = "%s/%s/%s/images/addon_icon/%%d-%%d.png?%%s"
settings.ADDON_ICON_URL = url_string % (
2010-12-30 20:54:28 +03:00
settings.STATIC_URL, settings.LANGUAGE_CODE, settings.DEFAULT_APP)
super(TestSubmitStep4, self).setUp()
SubmitStep.objects.create(addon_id=3615, step=5)
self.url = reverse('devhub.submit.4', args=['a3615'])
self.next_step = reverse('devhub.submit.5', args=['a3615'])
self.icon_upload = reverse('devhub.addons.upload_icon',
args=['a3615'])
self.preview_upload = reverse('devhub.addons.upload_preview',
args=['a3615'])
2010-12-30 20:54:28 +03:00
def tearDown(self):
settings.ADDON_ICON_URL = self.old_addon_icon_url
def test_get(self):
eq_(self.client.get(self.url).status_code, 200)
def test_post(self):
data = dict(icon_type='')
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
r = self.client.post(self.url, data_formset)
eq_(r.status_code, 302)
eq_(self.get_step().step, 5)
2010-12-31 04:02:31 +03:00
def formset_new_form(self, *args, **kw):
ctx = self.client.get(self.url).context
blank = initial(ctx['preview_form'].forms[-1])
blank.update(**kw)
return blank
def formset_media(self, *args, **kw):
kw.setdefault('initial_count', 0)
kw.setdefault('prefix', 'files')
fs = formset(*[a for a in args] + [self.formset_new_form()], **kw)
return dict([(k, '' if v is None else v) for k, v in fs.items()])
def test_edit_media_defaulticon(self):
data = dict(icon_type='')
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
self.client.post(self.url, data_formset)
addon = self.get_addon()
assert addon.get_icon_url(64).endswith('icons/default-64.png')
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_media_preuploadedicon(self):
data = dict(icon_type='icon/appearance')
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
self.client.post(self.url, data_formset)
addon = self.get_addon()
eq_('/'.join(addon.get_icon_url(64).split('/')[-2:]),
'addon-icons/appearance-64.png')
for k in data:
eq_(unicode(getattr(addon, k)), data[k])
def test_edit_media_uploadedicon(self):
img = get_image_path('mozilla.png')
src_image = open(img, 'rb')
data = dict(upload_image=src_image)
response = self.client.post(self.icon_upload, data)
response_json = json.loads(response.content)
addon = self.get_addon()
# Now, save the form so it gets moved properly.
data = dict(icon_type='image/png',
icon_upload_hash=response_json['upload_hash'])
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
2011-01-18 13:53:22 +03:00
self.client.post(self.url, data_formset)
addon = self.get_addon()
addon_url = addon.get_icon_url(64).split('?')[0]
assert addon_url.endswith('images/addon_icon/%s-64.png' % addon.id)
eq_(data['icon_type'], 'image/png')
# Check that it was actually uploaded
dirname = os.path.join(settings.ADDON_ICONS_PATH,
'%s' % (addon.id / 1000))
dest = os.path.join(dirname, '%s-32.png' % addon.id)
assert os.path.exists(dest)
eq_(Image.open(dest).size, (32, 12))
def test_edit_media_uploadedicon_noresize(self):
img = "%s/img/notifications/error.png" % settings.MEDIA_ROOT
src_image = open(img, 'rb')
data = dict(upload_image=src_image)
response = self.client.post(self.icon_upload, data)
response_json = json.loads(response.content)
addon = self.get_addon()
# Now, save the form so it gets moved properly.
data = dict(icon_type='image/png',
icon_upload_hash=response_json['upload_hash'])
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
2011-01-18 13:53:22 +03:00
self.client.post(self.url, data_formset)
addon = self.get_addon()
addon_url = addon.get_icon_url(64).split('?')[0]
assert addon_url.endswith('images/addon_icon/%s-64.png' % addon.id)
eq_(data['icon_type'], 'image/png')
# Check that it was actually uploaded
dirname = os.path.join(settings.ADDON_ICONS_PATH,
'%s' % (addon.id / 1000))
dest = os.path.join(dirname, '%s-64.png' % addon.id)
assert os.path.exists(dest)
eq_(Image.open(dest).size, (48, 48))
2010-12-10 22:52:26 +03:00
def test_client_lied(self):
filehandle = open(get_image_path('non-animated.gif'), 'rb')
2010-12-31 04:02:31 +03:00
data = {'upload_image': filehandle}
res = self.client.post(self.preview_upload, data)
response_json = json.loads(res.content)
eq_(response_json['errors'][0], u'Icons must be either PNG or JPG.')
2010-12-10 22:52:26 +03:00
def test_icon_animated(self):
filehandle = open(get_image_path('animated.png'), 'rb')
data = {'upload_image': filehandle}
res = self.client.post(self.preview_upload, data)
response_json = json.loads(res.content)
eq_(response_json['errors'][0], u'Icons cannot be animated.')
2010-12-10 22:52:26 +03:00
def test_icon_non_animated(self):
filehandle = open(get_image_path('non-animated.png'), 'rb')
data = {'icon_type': 'image/png', 'icon_upload': filehandle}
2010-12-31 04:02:31 +03:00
data_formset = self.formset_media(**data)
res = self.client.post(self.url, data_formset)
2010-12-10 22:52:26 +03:00
eq_(res.status_code, 302)
eq_(self.get_step().step, 5)
2011-01-18 13:53:22 +03:00
2010-11-18 02:28:49 +03:00
class TestSubmitStep5(TestSubmitBase):
"""License submission."""
2010-11-18 02:28:49 +03:00
def setUp(self):
super(TestSubmitStep5, self).setUp()
SubmitStep.objects.create(addon_id=3615, step=5)
self.url = reverse('devhub.submit.5', args=['a3615'])
self.next_step = reverse('devhub.submit.6', args=['a3615'])
2010-11-18 02:28:49 +03:00
License.objects.create(builtin=3, on_form=True)
def test_get(self):
eq_(self.client.get(self.url).status_code, 200)
def test_set_license(self):
r = self.client.post(self.url, {'builtin': 3})
self.assertRedirects(r, self.next_step)
eq_(self.get_addon().current_version.license.builtin, 3)
eq_(self.get_step().step, 6)
log_items = ActivityLog.objects.for_addons(self.get_addon())
assert not log_items.filter(action=amo.LOG.CHANGE_LICENSE.id), \
"Initial license choice:6 needn't be logged."
2010-11-18 02:28:49 +03:00
def test_license_error(self):
r = self.client.post(self.url, {'builtin': 4})
eq_(r.status_code, 200)
self.assertFormError(r, 'license_form', 'builtin',
'Select a valid choice. 4 is not one of '
'the available choices.')
eq_(self.get_step().step, 5)
def test_set_eula(self):
self.get_addon().update(eula=None, privacy_policy=None)
2010-11-18 02:28:49 +03:00
r = self.client.post(self.url, dict(builtin=3, has_eula=True,
eula='xxx'))
self.assertRedirects(r, self.next_step)
eq_(unicode(self.get_addon().eula), 'xxx')
eq_(self.get_step().step, 6)
def test_set_eula_nomsg(self):
"""
You should not get punished with a 500 for not writing your EULA...
but perhaps you should feel shame for lying to us. This test does not
test for shame.
"""
self.get_addon().update(eula=None, privacy_policy=None)
r = self.client.post(self.url, dict(builtin=3, has_eula=True))
self.assertRedirects(r, self.next_step)
eq_(self.get_step().step, 6)
2010-11-17 07:32:40 +03:00
2010-11-17 05:15:54 +03:00
class TestSubmitStep6(TestSubmitBase):
def setUp(self):
super(TestSubmitStep6, self).setUp()
SubmitStep.objects.create(addon_id=3615, step=6)
self.url = reverse('devhub.submit.6', args=['a3615'])
2010-11-17 05:15:54 +03:00
def test_get(self):
r = self.client.get(self.url)
eq_(r.status_code, 200)
2010-11-17 05:15:54 +03:00
def test_require_review_type(self):
r = self.client.post(self.url, {'dummy': 'text'})
eq_(r.status_code, 200)
self.assertFormError(r, 'review_type_form', 'review_type',
'A review type must be selected.')
2010-11-17 05:15:54 +03:00
def test_bad_review_type(self):
d = dict(review_type='jetsfool')
2010-11-17 05:15:54 +03:00
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'review_type_form', 'review_type',
'Select a valid choice. jetsfool is not one of '
'the available choices.')
2010-11-17 05:15:54 +03:00
def test_prelim_review(self):
d = dict(review_type=amo.STATUS_UNREVIEWED)
2010-11-17 05:15:54 +03:00
r = self.client.post(self.url, d)
eq_(r.status_code, 302)
eq_(self.get_addon().status, amo.STATUS_UNREVIEWED)
2010-11-18 02:28:49 +03:00
assert_raises(SubmitStep.DoesNotExist, self.get_step)
2010-11-17 05:15:54 +03:00
def test_full_review(self):
self.get_version().update(nomination=None)
d = dict(review_type=amo.STATUS_NOMINATED)
2010-11-17 05:15:54 +03:00
r = self.client.post(self.url, d)
eq_(r.status_code, 302)
addon = self.get_addon()
eq_(addon.status, amo.STATUS_NOMINATED)
2011-04-17 03:37:09 +04:00
assert close_to_now(self.get_version().nomination)
2010-11-18 02:28:49 +03:00
assert_raises(SubmitStep.DoesNotExist, self.get_step)
2010-11-17 05:15:54 +03:00
def test_nomination_date_is_only_set_once(self):
# This was a regression, see bug 632191.
# Nominate:
r = self.client.post(self.url, dict(review_type=amo.STATUS_NOMINATED))
eq_(r.status_code, 302)
nomdate = datetime.now() - timedelta(days=5)
self.get_version().update(nomination=nomdate, _signal=False)
# Update something else in the addon:
self.get_addon().update(slug='foobar')
eq_(self.get_version().nomination.timetuple()[0:5],
nomdate.timetuple()[0:5])
2010-11-17 05:15:54 +03:00
class TestSubmitStep7(TestSubmitBase):
def test_finish_submitting_addon(self):
addon = Addon.objects.get(
name__localized_string='Delicious Bookmarks')
eq_(addon.current_version.supported_platforms, [amo.PLATFORM_ALL])
response = self.client.get(reverse('devhub.submit.7', args=['a3615']))
eq_(response.status_code, 200)
doc = pq(response.content)
eq_(response.status_code, 200)
eq_(response.context['addon'].name.localized_string,
u"Delicious Bookmarks")
abs_url = settings.SITE_URL + "/en-US/firefox/addon/a3615/"
eq_(doc("a#submitted-addon-url").text().strip(), abs_url)
eq_(doc("a#submitted-addon-url").attr('href'),
"/en-US/firefox/addon/a3615/")
next_steps = doc(".done-next-steps li a")
# edit listing of freshly submitted add-on...
eq_(next_steps[0].attrib['href'],
reverse('devhub.addons.edit',
kwargs=dict(addon_id=addon.slug)))
# edit your developer profile...
eq_(next_steps[1].attrib['href'],
reverse('devhub.addons.profile', args=[addon.slug]))
# view wait times:
eq_(next_steps[3].attrib['href'],
"https://forums.addons.mozilla.org/viewforum.php?f=21")
def test_finish_submitting_platform_specific_addon(self):
# mac-only Add-on:
addon = Addon.objects.get(name__localized_string='Cooliris')
AddonUser.objects.create(user=UserProfile.objects.get(pk=55021),
addon=addon)
response = self.client.get(reverse('devhub.submit.7',
args=['cooliris']))
eq_(response.status_code, 200)
doc = pq(response.content)
next_steps = doc(".done-next-steps li a")
# upload more platform specific files...
eq_(next_steps[0].attrib['href'],
reverse('devhub.versions.edit', kwargs=dict(
addon_id=addon.slug,
version_id=addon.current_version.id)))
# edit listing of freshly submitted add-on...
eq_(next_steps[1].attrib['href'],
reverse('devhub.addons.edit',
kwargs=dict(addon_id=addon.slug)))
def test_finish_addon_for_prelim_review(self):
addon = Addon.objects.get(pk=3615)
addon.status = amo.STATUS_UNREVIEWED
addon.save()
response = self.client.get(reverse('devhub.submit.7', args=['a3615']))
eq_(response.status_code, 200)
doc = pq(response.content)
exp = 'Your add-on has been submitted to the Preliminary Review queue'
intro = doc('.addon-submission-process p').text()
assert exp in intro, ('Unexpected intro: %s' % intro.strip())
def test_finish_addon_for_full_review(self):
addon = Addon.objects.get(pk=3615)
addon.status = amo.STATUS_NOMINATED
addon.save()
response = self.client.get(reverse('devhub.submit.7', args=['a3615']))
eq_(response.status_code, 200)
doc = pq(response.content)
exp = 'Your add-on has been submitted to the Full Review queue'
intro = doc('.addon-submission-process p').text()
assert exp in intro, ('Unexpected intro: %s' % intro.strip())
2010-11-17 01:10:15 +03:00
def test_incomplete_addon_no_versions(self):
addon = Addon.objects.get(pk=3615)
addon.update(status=amo.STATUS_NULL)
addon.versions.all().delete()
r = self.client.get(reverse('devhub.submit.7', args=['a3615']),
follow=True)
self.assertRedirects(r, reverse('devhub.versions', args=['a3615']))
def test_link_to_activityfeed(self):
addon = Addon.objects.get(pk=3615)
r = self.client.get(reverse('devhub.submit.7', args=['a3615']),
follow=True)
doc = pq(r.content)
eq_(doc('.done-next-steps a').eq(2).attr('href'),
reverse('devhub.feed', args=[addon.slug]))
def test_display_non_ascii_url(self):
addon = Addon.objects.get(pk=3615)
u = 'フォクすけといっしょ'
addon.update(slug=u)
r = self.client.get(reverse('devhub.submit.7', args=[u]))
eq_(r.status_code, 200)
# The meta charset will always be utf-8.
doc = pq(r.content.decode('utf-8'))
eq_(doc('#submitted-addon-url').text(),
u'%s/en-US/firefox/addon/%s/' % (
settings.SITE_URL, u.decode('utf8')))
2010-11-17 01:10:15 +03:00
class TestResumeStep(TestSubmitBase):
def setUp(self):
super(TestResumeStep, self).setUp()
self.url = reverse('devhub.submit.resume', args=['a3615'])
def test_no_step_redirect(self):
r = self.client.get(self.url, follow=True)
self.assertRedirects(r, reverse('devhub.versions', args=['a3615']),
302)
def test_step_redirects(self):
SubmitStep.objects.create(addon_id=3615, step=1)
for i in xrange(3, 7):
SubmitStep.objects.filter(addon=self.get_addon()).update(step=i)
r = self.client.get(self.url, follow=True)
self.assertRedirects(r, reverse('devhub.submit.%s' % i,
args=['a3615']))
def test_redirect_from_other_pages(self):
SubmitStep.objects.create(addon_id=3615, step=4)
r = self.client.get(reverse('devhub.addons.edit', args=['a3615']),
follow=True)
self.assertRedirects(r, reverse('devhub.submit.4', args=['a3615']))
2011-01-18 13:53:22 +03:00
2010-11-17 05:15:54 +03:00
class TestSubmitSteps(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
assert self.client.login(username='del@icio.us', password='password')
def assert_linked(self, doc, numbers):
"""Check that the nth <li> in the steps list is a link."""
lis = doc('.submit-addon-progress li')
eq_(len(lis), 7)
for idx, li in enumerate(lis):
links = pq(li)('a')
if (idx + 1) in numbers:
eq_(len(links), 1)
else:
eq_(len(links), 0)
def assert_highlight(self, doc, num):
"""Check that the nth <li> is marked as .current."""
lis = doc('.submit-addon-progress li')
assert pq(lis[num - 1]).hasClass('current')
eq_(len(pq('.current', lis)), 1)
2010-11-17 05:15:54 +03:00
def test_step_1(self):
r = self.client.get(reverse('devhub.submit.1'))
2010-11-17 05:15:54 +03:00
eq_(r.status_code, 200)
def test_on_step_6(self):
# Hitting the step we're supposed to be on is a 200.
SubmitStep.objects.create(addon_id=3615, step=6)
r = self.client.get(reverse('devhub.submit.6',
args=['a3615']))
2010-11-17 05:15:54 +03:00
eq_(r.status_code, 200)
def test_skip_step_6(self):
# We get bounced back to step 3.
SubmitStep.objects.create(addon_id=3615, step=3)
r = self.client.get(reverse('devhub.submit.6',
args=['a3615']), follow=True)
self.assertRedirects(r, reverse('devhub.submit.3', args=['a3615']))
2010-11-17 05:15:54 +03:00
def test_all_done(self):
# There's no SubmitStep, so we must be done.
r = self.client.get(reverse('devhub.submit.6',
args=['a3615']), follow=True)
self.assertRedirects(r, reverse('devhub.submit.7', args=['a3615']))
def test_menu_step_1(self):
doc = pq(self.client.get(reverse('devhub.submit.1')).content)
self.assert_linked(doc, [1])
self.assert_highlight(doc, 1)
def test_menu_step_2(self):
2010-11-19 22:32:13 +03:00
self.client.post(reverse('devhub.submit.1'))
doc = pq(self.client.get(reverse('devhub.submit.2')).content)
self.assert_linked(doc, [1, 2])
self.assert_highlight(doc, 2)
def test_menu_step_3(self):
SubmitStep.objects.create(addon_id=3615, step=3)
url = reverse('devhub.submit.3', args=['a3615'])
doc = pq(self.client.get(url).content)
self.assert_linked(doc, [3])
self.assert_highlight(doc, 3)
def test_menu_step_3_from_6(self):
SubmitStep.objects.create(addon_id=3615, step=6)
url = reverse('devhub.submit.3', args=['a3615'])
doc = pq(self.client.get(url).content)
self.assert_linked(doc, [3, 4, 5, 6])
self.assert_highlight(doc, 3)
def test_menu_step_6(self):
SubmitStep.objects.create(addon_id=3615, step=6)
url = reverse('devhub.submit.6', args=['a3615'])
doc = pq(self.client.get(url).content)
self.assert_linked(doc, [3, 4, 5, 6])
self.assert_highlight(doc, 6)
def test_menu_step_7(self):
url = reverse('devhub.submit.7', args=['a3615'])
doc = pq(self.client.get(url).content)
self.assert_linked(doc, [])
self.assert_highlight(doc, 7)
class TestUpload(BaseUploadTest):
2010-11-20 05:46:12 +03:00
fixtures = ['base/apps', 'base/users']
def setUp(self):
super(TestUpload, self).setUp()
assert self.client.login(username='regular@mozilla.com',
password='password')
self.url = reverse('devhub.upload')
def post(self):
# Has to be a binary, non xpi file.
data = open(get_image_path('animated.png'), 'rb')
return self.client.post(self.url, {'upload': data})
def test_login_required(self):
self.client.logout()
r = self.post()
eq_(r.status_code, 302)
def test_create_fileupload(self):
self.post()
upload = FileUpload.objects.get(name='animated.png')
eq_(upload.name, 'animated.png')
data = open(get_image_path('animated.png'), 'rb').read()
eq_(open(upload.path).read(), data)
def test_fileupload_user(self):
self.client.login(username='regular@mozilla.com', password='password')
self.post()
user = UserProfile.objects.get(email='regular@mozilla.com')
eq_(FileUpload.objects.get().user, user)
def test_fileupload_ascii_post(self):
2011-01-27 03:33:44 +03:00
path = 'apps/files/fixtures/files/jétpack.xpi'
data = open(os.path.join(settings.ROOT, path))
r = self.client.post(self.url, {'upload': data})
# If this is broke, we'll get a traceback.
eq_(r.status_code, 302)
@attr('validator')
def test_fileupload_validation(self):
self.post()
fu = FileUpload.objects.get(name='animated.png')
assert_no_validation_errors(fu)
assert fu.validation
validation = json.loads(fu.validation)
eq_(validation['success'], False)
# The current interface depends on this JSON structure:
eq_(validation['errors'], 1)
eq_(validation['warnings'], 0)
assert len(validation['messages'])
msg = validation['messages'][0]
assert 'uid' in msg, "Unexpected: %r" % msg
eq_(msg['type'], u'error')
eq_(msg['message'], u'The package is not of a recognized type.')
eq_(msg['description'], u'')
def test_redirect(self):
r = self.post()
upload = FileUpload.objects.get()
url = reverse('devhub.upload_detail', args=[upload.pk, 'json'])
self.assertRedirects(r, url)
class TestUploadDetail(BaseUploadTest):
fixtures = ['base/apps', 'base/appversion', 'base/users']
def setUp(self):
super(TestUploadDetail, self).setUp()
assert self.client.login(username='regular@mozilla.com',
password='password')
def post(self):
# Has to be a binary, non xpi file.
data = open(get_image_path('animated.png'), 'rb')
return self.client.post(reverse('devhub.upload'), {'upload': data})
def validation_ok(self):
return {
'errors': 0,
'success': True,
'warnings': 0,
'notices': 0,
'message_tree': {},
'messages': [],
'rejected': False,
'metadata': {}}
def upload_file(self, file):
addon = os.path.join(settings.ROOT, 'apps', 'devhub', 'tests',
'addons', file)
with open(addon, 'rb') as f:
r = self.client.post(reverse('devhub.upload'),
{'upload': f})
eq_(r.status_code, 302)
@attr('validator')
def test_detail_json(self):
self.post()
upload = FileUpload.objects.get()
r = self.client.get(reverse('devhub.upload_detail',
args=[upload.uuid, 'json']))
eq_(r.status_code, 200)
data = json.loads(r.content)
assert_no_validation_errors(data)
eq_(data['url'],
reverse('devhub.upload_detail', args=[upload.uuid, 'json']))
eq_(data['full_report_url'],
reverse('devhub.upload_detail', args=[upload.uuid]))
# We must have tiers
assert len(data['validation']['messages'])
msg = data['validation']['messages'][0]
eq_(msg['tier'], 1)
def test_detail_view(self):
self.post()
upload = FileUpload.objects.get(name='animated.png')
r = self.client.get(reverse('devhub.upload_detail',
args=[upload.uuid]))
eq_(r.status_code, 200)
doc = pq(r.content)
eq_(doc('header h2').text(), 'Validation Results for animated.png')
suite = doc('#addon-validator-suite')
eq_(suite.attr('data-validateurl'),
reverse('devhub.standalone_upload_detail', args=[upload.uuid]))
@mock.patch('devhub.tasks.run_validator')
def test_multi_app_addon_can_have_all_platforms(self, v):
v.return_value = json.dumps(self.validation_ok())
self.upload_file('mobile-2.9.10-fx+fn.xpi')
upload = FileUpload.objects.get()
r = self.client.get(reverse('devhub.upload_detail',
args=[upload.uuid, 'json']))
eq_(r.status_code, 200)
data = json.loads(r.content)
eq_(data['platforms_to_exclude'], [])
@mock.patch('devhub.tasks.run_validator')
def test_mobile_excludes_desktop_platforms(self, v):
v.return_value = json.dumps(self.validation_ok())
self.upload_file('mobile-0.1-fn.xpi')
upload = FileUpload.objects.get()
r = self.client.get(reverse('devhub.upload_detail',
args=[upload.uuid, 'json']))
eq_(r.status_code, 200)
data = json.loads(r.content)
eq_(sorted(data['platforms_to_exclude']),
sorted([str(p) for p in amo.DESKTOP_PLATFORMS]))
@mock.patch('devhub.tasks.run_validator')
def test_search_tool_excludes_all_platforms(self, v):
v.return_value = json.dumps(self.validation_ok())
self.upload_file('searchgeek-20090701.xml')
upload = FileUpload.objects.get()
r = self.client.get(reverse('devhub.upload_detail',
args=[upload.uuid, 'json']))
eq_(r.status_code, 200)
data = json.loads(r.content)
eq_(sorted(data['platforms_to_exclude']),
sorted([str(p) for p in amo.SUPPORTED_PLATFORMS]))
@mock.patch('devhub.tasks.run_validator')
def test_desktop_excludes_mobile(self, v):
v.return_value = json.dumps(self.validation_ok())
self.upload_file('desktop.xpi')
upload = FileUpload.objects.get()
r = self.client.get(reverse('devhub.upload_detail',
args=[upload.uuid, 'json']))
eq_(r.status_code, 200)
data = json.loads(r.content)
eq_(sorted(data['platforms_to_exclude']),
sorted([str(p) for p in amo.MOBILE_PLATFORMS]))
@mock.patch('devhub.tasks.run_validator')
@mock.patch.object(waffle, 'flag_is_active')
def test_unparsable_xpi(self, flag_is_active, v):
flag_is_active.return_value = True
v.return_value = json.dumps(self.validation_ok())
self.upload_file('unopenable.xpi')
upload = FileUpload.objects.get()
r = self.client.get(reverse('devhub.upload_detail',
args=[upload.uuid, 'json']))
data = json.loads(r.content)
eq_(list(m['message'] for m in data['validation']['messages']),
[u'Could not parse install.rdf.'])
2010-11-23 04:45:12 +03:00
def assert_json_error(request, field, msg):
eq_(request.status_code, 400)
eq_(request['Content-Type'], 'application/json')
field = '__all__' if field is None else field
content = json.loads(request.content)
assert field in content, '%r not in %r' % (field, content)
eq_(content[field], [msg])
2010-12-31 00:44:34 +03:00
def assert_json_field(request, field, msg):
eq_(request.status_code, 200)
eq_(request['Content-Type'], 'application/json')
content = json.loads(request.content)
assert field in content, '%r not in %r' % (field, content)
eq_(content[field], msg)
class UploadTest(BaseUploadTest, test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
super(UploadTest, self).setUp()
self.upload = self.get_upload('extension.xpi')
self.addon = Addon.objects.get(id=3615)
self.version = self.addon.current_version
self.addon.update(guid='guid@xpi')
2010-12-21 01:45:02 +03:00
if not Platform.objects.filter(id=amo.PLATFORM_MAC.id):
Platform.objects.create(id=amo.PLATFORM_MAC.id)
assert self.client.login(username='del@icio.us', password='password')
class TestQueuePosition(UploadTest):
fixtures = ['base/apps', 'base/users',
'base/addon_3615', 'base/platforms']
def setUp(self):
super(TestQueuePosition, self).setUp()
self.url = reverse('devhub.versions.add_file',
args=[self.addon.slug, self.version.id])
self.edit_url = reverse('devhub.versions.edit',
args=[self.addon.slug, self.version.id])
files = self.version.files.all()[0]
files.platform_id = amo.PLATFORM_LINUX.id
files.save()
def test_not_in_queue(self):
r = self.client.get(reverse('devhub.versions', args=[self.addon.slug]))
eq_(self.addon.status, amo.STATUS_PUBLIC)
eq_(pq(r.content)('.version-status-actions .dark').length, 0)
def test_in_queue(self):
statuses = [(amo.STATUS_NOMINATED, amo.STATUS_NOMINATED),
(amo.STATUS_PUBLIC, amo.STATUS_UNREVIEWED),
(amo.STATUS_LITE, amo.STATUS_UNREVIEWED)]
for addon_status in statuses:
self.addon.status = addon_status[0]
self.addon.save()
file = self.addon.latest_version.files.all()[0]
file.status = addon_status[1]
file.save()
r = self.client.get(reverse('devhub.versions',
args=[self.addon.slug]))
doc = pq(r.content)
span = doc('.version-status-actions .dark')
eq_(span.length, 1)
assert "Queue Position: 1 of 1" in span.text()
class TestVersionAddFile(UploadTest):
2010-12-21 01:45:02 +03:00
fixtures = ['base/apps', 'base/users',
'base/addon_3615', 'base/platforms']
def setUp(self):
super(TestVersionAddFile, self).setUp()
self.version.update(version='0.1')
self.url = reverse('devhub.versions.add_file',
args=[self.addon.slug, self.version.id])
self.edit_url = reverse('devhub.versions.edit',
args=[self.addon.slug, self.version.id])
2010-12-21 01:45:02 +03:00
files = self.version.files.all()[0]
files.platform_id = amo.PLATFORM_LINUX.id
files.save()
def make_mobile(self):
app = Application.objects.get(pk=amo.MOBILE.id)
for a in self.version.apps.all():
a.application = app
a.save()
def post(self, platform=amo.PLATFORM_MAC):
return self.client.post(self.url, dict(upload=self.upload.pk,
platform=platform.id))
def test_guid_matches(self):
self.addon.update(guid='something.different')
r = self.post()
assert_json_error(r, None, "UUID doesn't match add-on.")
def test_version_matches(self):
self.version.update(version='2.0')
r = self.post()
2010-11-23 04:45:12 +03:00
assert_json_error(r, None, "Version doesn't match")
def test_delete_button_enabled(self):
version = self.addon.current_version
version.files.all()[0].update(status=amo.STATUS_UNREVIEWED)
r = self.client.get(self.edit_url)
doc = pq(r.content)('#file-list')
eq_(doc.find('a.remove').length, 1)
eq_(doc.find('span.remove.tooltip').length, 0)
def test_delete_button_disabled(self):
r = self.client.get(self.edit_url)
doc = pq(r.content)('#file-list')
eq_(doc.find('a.remove').length, 0)
eq_(doc.find('span.remove.tooltip').length, 1)
tip = doc.find('span.remove.tooltip')
assert "You cannot remove an individual file" in tip.attr('title')
def test_delete_button_multiple(self):
file = self.addon.current_version.files.all()[0]
file.pk = None
file.save()
cases = [(amo.STATUS_UNREVIEWED, amo.STATUS_UNREVIEWED, True),
(amo.STATUS_LISTED, amo.STATUS_UNREVIEWED, False),
(amo.STATUS_LISTED, amo.STATUS_LISTED, False)]
for c in cases:
files = self.addon.current_version.files.all()
files[0].update(status=c[0])
files[1].update(status=c[1])
r = self.client.get(self.edit_url)
doc = pq(r.content)('#file-list')
assert (doc.find('a.remove').length > 0) == c[2]
assert not (doc.find('span.remove').length > 0) == c[2]
if not c[2]:
tip = doc.find('span.remove.tooltip')
assert "You cannot remove an individual" in tip.attr('title')
def test_delete_submit_disabled(self):
file_id = self.addon.current_version.files.all()[0].id
platform = amo.PLATFORM_MAC.id
form = {'DELETE': 'checked', 'id': file_id, 'platform': platform}
data = formset(form, platform=platform, upload=self.upload.pk,
initial_count=1, prefix='files')
r = self.client.post(self.edit_url, data)
doc = pq(r.content)
assert "You cannot delete a file once" in doc('.errorlist li').text()
def test_delete_submit_enabled(self):
version = self.addon.current_version
version.files.all()[0].update(status=amo.STATUS_UNREVIEWED)
file_id = self.addon.current_version.files.all()[0].id
platform = amo.PLATFORM_MAC.id
form = {'DELETE': 'checked', 'id': file_id, 'platform': platform}
data = formset(form, platform=platform, upload=self.upload.pk,
initial_count=1, prefix='files')
r = self.client.post(self.edit_url, data)
doc = pq(r.content)
eq_(doc('.errorlist li').length, 0)
def test_platform_limits(self):
2010-12-21 01:45:02 +03:00
r = self.post(platform=amo.PLATFORM_BSD)
2010-11-23 04:45:12 +03:00
assert_json_error(r, 'platform',
'Select a valid choice. That choice is not '
'one of the available choices.')
def test_platform_choices(self):
r = self.client.get(self.edit_url)
form = r.context['new_file_form']
platform = self.version.files.get().platform_id
choices = form.fields['platform'].choices
# User cannot upload existing platforms:
assert platform not in dict(choices), choices
# User cannot upload platform=ALL when platform files exist.
assert amo.PLATFORM_ALL.id not in dict(choices), choices
def test_platform_choices_when_no_files(self):
all_choices = self.version.compatible_platforms().values()
self.version.files.all().delete()
url = reverse('devhub.versions.edit',
args=[self.addon.slug, self.version.id])
r = self.client.get(url)
form = r.context['new_file_form']
eq_(sorted(dict(form.fields['platform'].choices).keys()),
sorted([p.id for p in all_choices]))
def test_platform_choices_when_mobile(self):
self.make_mobile()
self.version.files.all().delete()
r = self.client.get(self.edit_url)
form = r.context['new_file_form']
# TODO(Kumar) Allow All Mobile Platforms when supported for downloads.
# See bug 646268.
exp_plats = (set(amo.MOBILE_PLATFORMS.values()) -
set([amo.PLATFORM_ALL_MOBILE]))
eq_(sorted([unicode(c[1]) for c in form.fields['platform'].choices]),
sorted([unicode(p.name) for p in exp_plats]))
def test_exclude_mobile_all_when_we_have_platform_files(self):
self.make_mobile()
# set one to Android
self.version.files.all().update(platform=amo.PLATFORM_ANDROID.id)
r = self.post(platform=amo.PLATFORM_ALL_MOBILE)
assert_json_error(r, 'platform',
'Select a valid choice. That choice is not '
'one of the available choices.')
def test_type_matches(self):
self.addon.update(type=amo.ADDON_THEME)
r = self.post()
2010-11-23 04:45:12 +03:00
assert_json_error(r, None, "<em:type> doesn't match add-on")
def test_file_platform(self):
# Check that we're creating a new file with the requested platform.
qs = self.version.files
eq_(len(qs.all()), 1)
assert not qs.filter(platform=amo.PLATFORM_MAC.id)
self.post()
eq_(len(qs.all()), 2)
assert qs.get(platform=amo.PLATFORM_MAC.id)
def test_upload_not_found(self):
r = self.client.post(self.url, dict(upload='xxx',
platform=amo.PLATFORM_MAC.id))
2010-11-23 04:45:12 +03:00
assert_json_error(r, 'upload',
'There was an error with your upload. '
'Please try again.')
@mock.patch('versions.models.Version.is_allowed_upload')
def test_cant_upload(self, allowed):
"""Test that if is_allowed_upload fails, the upload will fail."""
allowed.return_value = False
res = self.post()
assert_json_error(res, '__all__',
'You cannot upload any more files for this version.')
def test_success_html(self):
r = self.post()
eq_(r.status_code, 200)
new_file = self.version.files.get(platform=amo.PLATFORM_MAC.id)
eq_(r.context['form'].instance, new_file)
def test_show_item_history(self):
version = self.addon.current_version
user = UserProfile.objects.get(email='editor@mozilla.com')
details = {'comments': 'yo', 'files': [version.files.all()[0].id]}
amo.log(amo.LOG.APPROVE_VERSION, self.addon,
self.addon.current_version, user=user, created=datetime.now(),
details=details)
doc = pq(self.client.get(self.edit_url).content)
appr = doc('#approval_status')
eq_(appr.length, 1)
eq_(appr.find('strong').eq(0).text(), "File (Linux)")
eq_(appr.find('.version-comments').length, 1)
comment = appr.find('.version-comments').eq(0)
eq_(comment.find('strong a').text(), "Delicious Bookmarks Version 0.1")
eq_(comment.find('div.email_comment').length, 1)
eq_(comment.find('div').eq(1).text(), "yo")
def test_show_item_history_hide_message(self):
""" Test to make sure comments not to the user aren't shown. """
version = self.addon.current_version
user = UserProfile.objects.get(email='editor@mozilla.com')
details = {'comments': 'yo', 'files': [version.files.all()[0].id]}
amo.log(amo.LOG.REQUEST_SUPER_REVIEW, self.addon,
self.addon.current_version, user=user, created=datetime.now(),
details=details)
doc = pq(self.client.get(self.edit_url).content)
comment = doc('#approval_status').find('.version-comments').eq(0)
eq_(comment.find('div.email_comment').length, 0)
def test_show_item_history_multiple(self):
version = self.addon.current_version
user = UserProfile.objects.get(email='editor@mozilla.com')
details = {'comments': 'yo', 'files': [version.files.all()[0].id]}
amo.log(amo.LOG.APPROVE_VERSION, self.addon,
self.addon.current_version, user=user, created=datetime.now(),
details=details)
amo.log(amo.LOG.REQUEST_SUPER_REVIEW, self.addon,
self.addon.current_version, user=user, created=datetime.now(),
details=details)
doc = pq(self.client.get(self.edit_url).content)
comments = doc('#approval_status').find('.version-comments')
eq_(comments.length, 2)
class TestUploadErrors(UploadTest):
fixtures = ['base/apps', 'base/users',
'base/addon_3615', 'base/platforms']
validator_success = json.dumps({
"errors": 0,
"success": True,
"warnings": 0,
"notices": 0,
"message_tree": {},
"messages": [],
"metadata": {}
})
def xpi(self):
return open(os.path.join(os.path.dirname(files.__file__),
'fixtures', 'files',
'delicious_bookmarks-2.1.106-fx.xpi'),
'rb')
@mock.patch.object(waffle, 'flag_is_active')
@mock.patch('devhub.tasks.run_validator')
def test_version_upload(self, run_validator, flag_is_active):
run_validator.return_value = ''
flag_is_active.return_value = True
# Load the versions page:
res = self.client.get(reverse('devhub.versions',
args=[self.addon.slug]))
eq_(res.status_code, 200)
doc = pq(res.content)
# javascript: upload file:
upload_url = doc('#upload-addon').attr('data-upload-url')
with self.xpi() as f:
res = self.client.post(upload_url, {'upload': f}, follow=True)
data = json.loads(res.content)
# Simulate the validation task finishing after a delay:
run_validator.return_value = self.validator_success
tasks.validator.delay(data['upload'])
# javascript: poll for status:
res = self.client.get(data['url'])
data = json.loads(res.content)
if data['validation'] and data['validation']['messages']:
raise AssertionError('Unexpected validation errors: %s'
% data['validation']['messages'])
@mock.patch.object(waffle, 'flag_is_active')
@mock.patch('devhub.tasks.run_validator')
def test_dupe_xpi(self, run_validator, flag_is_active):
run_validator.return_value = ''
flag_is_active.return_value = True
# Submit a new addon:
self.client.post(reverse('devhub.submit.1')) # set cookie
res = self.client.get(reverse('devhub.submit.2'))
eq_(res.status_code, 200)
doc = pq(res.content)
# javascript: upoad file:
upload_url = doc('#upload-addon').attr('data-upload-url')
with self.xpi() as f:
res = self.client.post(upload_url, {'upload': f}, follow=True)
data = json.loads(res.content)
# Simulate the validation task finishing after a delay:
run_validator.return_value = self.validator_success
tasks.validator.delay(data['upload'])
# javascript: poll for results:
res = self.client.get(data['url'])
data = json.loads(res.content)
eq_(list(m['message'] for m in data['validation']['messages']),
[u'Duplicate UUID found.'])
class TestAddVersion(UploadTest):
def post(self, desktop_platforms=[amo.PLATFORM_MAC], mobile_platforms=[],
expected_status=200):
d = dict(upload=self.upload.pk,
desktop_platforms=[p.id for p in desktop_platforms],
mobile_platforms=[p.id for p in mobile_platforms])
r = self.client.post(self.url, d)
eq_(r.status_code, expected_status)
return r
def setUp(self):
super(TestAddVersion, self).setUp()
self.url = reverse('devhub.versions.add', args=[self.addon.slug])
def test_unique_version_num(self):
self.version.update(version='0.1')
r = self.post(expected_status=400)
2010-11-23 04:45:12 +03:00
assert_json_error(r, None, 'Version 0.1 already exists')
def test_success(self):
r = self.post()
version = self.addon.versions.get(version='0.1')
2010-12-31 00:44:34 +03:00
assert_json_field(r, 'url', reverse('devhub.versions.edit',
args=[self.addon.slug, version.id]))
2010-11-23 04:45:12 +03:00
def test_public(self):
self.post()
fle = File.objects.all().order_by("-created")[0]
eq_(fle.status, amo.STATUS_PUBLIC)
def test_not_public(self):
self.addon.update(trusted=False)
self.post()
fle = File.objects.all().order_by("-created")[0]
assert_not_equal(fle.status, amo.STATUS_PUBLIC)
def test_multiple_platforms(self):
r = self.post(desktop_platforms=[amo.PLATFORM_MAC,
amo.PLATFORM_LINUX])
eq_(r.status_code, 200)
version = self.addon.versions.get(version='0.1')
eq_(len(version.all_files), 2)
2010-11-23 04:45:12 +03:00
class TestVersionXSS(UploadTest):
def test_unique_version_num(self):
self.version.update(
version='<script>alert("Happy XSS-Xmas");</script>')
r = self.client.get(reverse('devhub.addons'))
eq_(r.status_code, 200)
assert '<script>alert' not in r.content
assert '&lt;script&gt;alert' in r.content
class TestCreateAddon(BaseUploadTest,
test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/platforms']
2010-11-23 04:45:12 +03:00
def setUp(self):
super(TestCreateAddon, self).setUp()
self.upload = self.get_upload('extension.xpi')
2010-11-23 04:45:12 +03:00
self.url = reverse('devhub.submit.2')
assert self.client.login(username='regular@mozilla.com',
password='password')
self.client.post(reverse('devhub.submit.1'))
def post(self, desktop_platforms=[amo.PLATFORM_ALL], mobile_platforms=[],
expect_errors=False):
d = dict(upload=self.upload.pk,
desktop_platforms=[p.id for p in desktop_platforms],
mobile_platforms=[p.id for p in mobile_platforms])
r = self.client.post(self.url, d, follow=True)
eq_(r.status_code, 200)
if not expect_errors:
# Show any unexpected form errors.
if r.context and 'new_addon_form' in r.context:
eq_(r.context['new_addon_form'].errors.as_text(), '')
return r
2010-11-23 04:45:12 +03:00
def assert_json_error(self, *args):
UploadTest().assert_json_error(self, *args)
def test_unique_name(self):
ReverseNameLookup().add('xpi name', 34)
r = self.post(expect_errors=True)
eq_(r.context['new_addon_form'].non_field_errors(),
['This add-on name is already in use. '
'Please choose another.'])
2010-11-23 04:45:12 +03:00
def test_success(self):
eq_(Addon.objects.count(), 0)
r = self.post()
addon = Addon.objects.get()
self.assertRedirects(r, reverse('devhub.submit.3',
args=[addon.slug]))
log_items = ActivityLog.objects.for_addons(addon)
assert log_items.filter(action=amo.LOG.CREATE_ADDON.id), \
'New add-on creation never logged.'
def test_missing_platforms(self):
r = self.client.post(self.url, dict(upload=self.upload.pk))
eq_(r.status_code, 200)
eq_(r.context['new_addon_form'].errors.as_text(),
'* __all__\n * Need at least one platform.')
doc = pq(r.content)
eq_(doc('ul.errorlist').text(),
'Need at least one platform.')
def test_one_xpi_for_multiple_platforms(self):
eq_(Addon.objects.count(), 0)
r = self.post(desktop_platforms=[amo.PLATFORM_MAC,
amo.PLATFORM_LINUX])
addon = Addon.objects.get()
self.assertRedirects(r, reverse('devhub.submit.3',
args=[addon.slug]))
eq_(sorted([f.filename for f in addon.current_version.all_files]),
[u'xpi_name-0.1-linux.xpi', u'xpi_name-0.1-mac.xpi'])
class TestDeleteAddon(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
super(TestDeleteAddon, self).setUp()
self.url = reverse('devhub.addons.delete', args=['a3615'])
assert self.client.login(username='del@icio.us', password='password')
self.addon = Addon.objects.get(id=3615)
def post(self, *args, **kw):
r = self.client.post(self.url, dict(*args, **kw))
eq_(r.status_code, 302)
return r
def test_bad_password(self):
r = self.post(password='turd')
eq_(r.context['title'],
'Password was incorrect. Add-on was not deleted.')
eq_(Addon.objects.count(), 1)
def test_success(self):
r = self.post(password='password')
eq_(r.context['title'], 'Add-on deleted.')
eq_(Addon.objects.count(), 0)
self.assertRedirects(r, reverse('devhub.addons'))
2010-12-10 22:21:26 +03:00
class TestRequestReview(test_utils.TestCase):
2010-12-30 00:57:50 +03:00
fixtures = ['base/users', 'base/platforms']
2010-12-10 22:21:26 +03:00
def setUp(self):
self.addon = Addon.objects.create(type=1, name='xxx')
self.version = Version.objects.create(addon=self.addon)
2010-12-30 00:57:50 +03:00
self.file = File.objects.create(version=self.version,
platform_id=amo.PLATFORM_ALL.id)
self.redirect_url = reverse('devhub.versions', args=[self.addon.slug])
2010-12-10 22:21:26 +03:00
self.lite_url = reverse('devhub.request-review',
args=[self.addon.slug, amo.STATUS_LITE])
2010-12-10 22:21:26 +03:00
self.public_url = reverse('devhub.request-review',
args=[self.addon.slug, amo.STATUS_PUBLIC])
2010-12-10 22:21:26 +03:00
assert self.client.login(username='admin@mozilla.com',
password='password')
def get_addon(self):
return Addon.objects.get(id=self.addon.id)
def get_version(self):
return Version.objects.get(pk=self.version.id)
2010-12-10 22:21:26 +03:00
def check(self, old_status, url, new_status):
self.addon.update(status=old_status)
r = self.client.post(url)
self.assertRedirects(r, self.redirect_url)
eq_(self.get_addon().status, new_status)
def check_400(self, url):
r = self.client.post(url)
eq_(r.status_code, 400)
def test_404(self):
bad_url = self.public_url.replace(str(amo.STATUS_PUBLIC), '0')
eq_(self.client.post(bad_url).status_code, 404)
def test_public(self):
self.addon.update(status=amo.STATUS_PUBLIC)
self.check_400(self.lite_url)
self.check_400(self.public_url)
def test_disabled_by_user_to_lite(self):
self.addon.update(disabled_by_user=True)
self.check_400(self.lite_url)
def test_disabled_by_admin(self):
self.addon.update(status=amo.STATUS_DISABLED)
self.check_400(self.lite_url)
def test_lite_to_lite(self):
self.addon.update(status=amo.STATUS_LITE)
self.check_400(self.lite_url)
def test_lite_to_public(self):
eq_(self.version.nomination, None)
2010-12-10 22:21:26 +03:00
self.check(amo.STATUS_LITE, self.public_url,
amo.STATUS_LITE_AND_NOMINATED)
2011-04-17 03:37:09 +04:00
assert close_to_now(self.get_version().nomination)
2010-12-10 22:21:26 +03:00
def test_purgatory_to_lite(self):
self.check(amo.STATUS_PURGATORY, self.lite_url, amo.STATUS_UNREVIEWED)
def test_purgatory_to_public(self):
eq_(self.version.nomination, None)
self.check(amo.STATUS_PURGATORY, self.public_url,
amo.STATUS_NOMINATED)
2011-04-17 03:37:09 +04:00
assert close_to_now(self.get_version().nomination)
2010-12-10 22:21:26 +03:00
def test_lite_and_nominated_to_public(self):
self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED)
self.check_400(self.public_url)
def test_lite_and_nominated(self):
self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED)
self.check_400(self.lite_url)
self.check_400(self.public_url)
2011-01-06 04:57:00 +03:00
def test_renominate_for_full_review(self):
# When a version is rejected, the addon is disabled.
# The author must upload a new version and re-nominate.
# However, renominating the *same* version does not adjust the
# nomination date.
orig_date = datetime.now() - timedelta(days=30)
# Pretend it was nominated in the past:
self.version.update(nomination=orig_date)
self.check(amo.STATUS_NULL, self.public_url, amo.STATUS_NOMINATED)
eq_(self.get_version().nomination.timetuple()[0:5],
orig_date.timetuple()[0:5])
def test_renomination_doesnt_reset_nomination_date(self):
# Nominate:
self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED)
# Pretend it was nominated in the past:
orig_date = datetime.now() - timedelta(days=30)
self.version.update(nomination=orig_date, _signal=False)
# Reject it:
self.addon.update(status=amo.STATUS_NULL)
# Re-nominate:
self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED)
eq_(self.get_version().nomination.timetuple()[0:5],
orig_date.timetuple()[0:5])
2011-01-06 04:57:00 +03:00
class TestRedirects(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
self.base = reverse('devhub.index')
assert self.client.login(username='admin@mozilla.com',
password='password')
def test_edit(self):
url = self.base + 'addon/edit/3615'
r = self.client.get(url, follow=True)
self.assertRedirects(r, reverse('devhub.addons.edit', args=['a3615']),
301)
url = self.base + 'addon/edit/3615/'
r = self.client.get(url, follow=True)
self.assertRedirects(r, reverse('devhub.addons.edit', args=['a3615']),
301)
def test_status(self):
url = self.base + 'addon/status/3615'
r = self.client.get(url, follow=True)
self.assertRedirects(r, reverse('devhub.versions', args=['a3615']),
301)
def test_versions(self):
url = self.base + 'versions/3615'
r = self.client.get(url, follow=True)
self.assertRedirects(r, reverse('devhub.versions', args=['a3615']),
301)
class TestAdmin(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def login_admin(self):
assert self.client.login(username='admin@mozilla.com',
password='password')
def login_user(self):
assert self.client.login(username='del@icio.us', password='password')
def test_show_admin_settings_admin(self):
self.login_admin()
url = reverse('devhub.addons.edit', args=['a3615'])
r = self.client.get(url)
eq_(r.status_code, 200)
self.assertContains(r, 'Admin Settings')
def test_show_admin_settings_nonadmin(self):
self.login_user()
url = reverse('devhub.addons.edit', args=['a3615'])
r = self.client.get(url)
eq_(r.status_code, 200)
self.assertNotContains(r, 'Admin Settings')
def test_post_as_admin(self):
self.login_admin()
url = reverse('devhub.addons.admin', args=['a3615'])
r = self.client.post(url)
eq_(r.status_code, 200)
def test_post_as_nonadmin(self):
self.login_user()
url = reverse('devhub.addons.admin', args=['a3615'])
r = self.client.post(url)
eq_(r.status_code, 403)
class TestNewsletter(test_utils.TestCase):
def test_get(self):
r = self.client.get(reverse('devhub.community.newsletter'))
eq_(r.status_code, 200)
@mock.patch('devhub.responsys.urllib2.urlopen')
def test_post(self, v):
v.return_value = namedtuple('_', 'code')
v.return_value.code = 200
email = 'test@example.com'
url = reverse('devhub.community.newsletter')
r = self.client.post(url, {'email': email, 'region': 'us',
'format': 'html', 'policy': 't'})
eq_(r.status_code, 302)
# Test call to responsys
eq_(v.call_args[0], ('http://awesomeness.mozilla.org/pub/rf',))
assert(urlencode({'EMAIL_ADDRESS_': email}) in v.call_args[1]['data'])
class TestDocs(test_utils.TestCase):
def test_doc_urls(self):
eq_('/en-US/developers/docs/', reverse('devhub.docs', args=[]))
eq_('/en-US/developers/docs/te', reverse('devhub.docs', args=['te']))
eq_('/en-US/developers/docs/te/st', reverse('devhub.docs',
args=['te', 'st']))
urls = [(reverse('devhub.docs', args=["getting-started"]), 200),
(reverse('devhub.docs', args=["how-to"]), 200),
(reverse('devhub.docs', args=["how-to", "other-addons"]), 200),
(reverse('devhub.docs', args=["fake-page"]), 302),
(reverse('devhub.docs', args=["how-to", "fake-page"]), 200),
(reverse('devhub.docs'), 302)]
index = reverse('devhub.index')
for url in urls:
r = self.client.get(url[0])
eq_(r.status_code, url[1])
if url[1] == 302: # Redirect to the index page
self.assertRedirects(r, index)
class TestRemoveLocale(test_utils.TestCase):
fixtures = ['base/apps', 'base/users', 'base/addon_3615']
def setUp(self):
self.addon = Addon.objects.get(id=3615)
self.url = reverse('devhub.remove-locale', args=['a3615'])
assert self.client.login(username='del@icio.us', password='password')
def test_bad_request(self):
r = self.client.post(self.url)
eq_(r.status_code, 400)
def test_success(self):
self.addon.name = {'en-US': 'woo', 'el': 'yeah'}
self.addon.save()
self.addon.remove_locale('el')
qs = (Translation.objects.filter(localized_string__isnull=False)
.values_list('locale', flat=True))
r = self.client.post(self.url, {'locale': 'el'})
eq_(r.status_code, 200)
eq_(sorted(qs.filter(id=self.addon.name_id)), ['en-US'])
def test_delete_default_locale(self):
r = self.client.post(self.url, {'locale': self.addon.default_locale})
eq_(r.status_code, 400)
def test_remove_version_locale(self):
version = self.addon.versions.all()[0]
version.releasenotes = {'fr': 'oui'}
version.save()
self.client.post(self.url, {'locale': 'fr'})
res = self.client.get(reverse('devhub.versions.edit',
args=[self.addon.slug, version.pk]))
doc = pq(res.content)
# There's 2 fields, one for en-us, one for init.
eq_(len(doc('div.trans textarea')), 2)