addons-server/mkt/submit/tests/test_views.py

1021 строка
40 KiB
Python

# -*- coding: utf-8 -*-
import json
import os
from django.conf import settings
import mock
from nose.tools import eq_
from pyquery import PyQuery as pq
import waffle
import amo
from amo.helpers import urlparams
import amo.tests
from amo.tests import formset, initial
from amo.tests.test_helpers import get_image_path
from amo.urlresolvers import reverse
from addons.models import (Addon, AddonCategory, AddonDeviceType, AddonUser,
Category, DeviceType)
from addons.utils import ReverseNameLookup
from apps.users.models import UserNotification
from apps.users.notifications import app_surveys
from files.tests.test_models import UploadTest as BaseUploadTest
from market.models import Price
import mkt
from mkt.submit.models import AppSubmissionChecklist
import paypal
from translations.models import Translation
from users.models import UserProfile
from mkt.webapps.models import Webapp
class TestSubmit(amo.tests.TestCase):
fixtures = ['base/users']
def setUp(self):
self.user = self.get_user()
assert self.client.login(username=self.user.email, password='password')
def get_user(self):
return UserProfile.objects.get(username='regularuser')
def _test_anonymous(self):
self.client.logout()
r = self.client.get(self.url, follow=True)
self.assertLoginRedirects(r, self.url)
def _test_progress_display(self, completed, current):
"""Test that the correct steps are highlighted."""
r = self.client.get(self.url)
progress = pq(r.content)('#submission-progress')
# Check the completed steps.
completed_found = progress.find('.completed')
for idx, step in enumerate(completed):
li = completed_found.eq(idx)
eq_(li.text(), unicode(mkt.APP_STEPS_TITLE[step]))
# Check that we link back to the Developer Agreement.
terms_link = progress.find('.terms a')
if 'terms' in completed:
eq_(terms_link.attr('href'),
reverse('mkt.developers.docs', args=['policies', 'agreement']))
else:
eq_(terms_link.length, 0)
# Check the current step.
eq_(progress.find('.current').text(),
unicode(mkt.APP_STEPS_TITLE[current]))
class TestTerms(TestSubmit):
fixtures = ['base/users']
def setUp(self):
super(TestTerms, self).setUp()
self.user.update(read_dev_agreement=False)
self.url = reverse('submit.app.terms')
def test_anonymous(self):
self.client.logout()
r = self.client.get(self.url, follow=True)
self.assertLoginRedirects(r, self.url)
def test_jump_to_step(self):
r = self.client.get(reverse('submit.app'), follow=True)
self.assertRedirects(r, self.url)
def test_page(self):
r = self.client.get(self.url)
eq_(r.status_code, 200)
doc = pq(r.content)('#submit-terms')
eq_(doc.length, 1)
eq_(doc.find('input[name=newsletter]').siblings('label').length, 1,
'Missing its <label>!')
def test_progress_display(self):
self._test_progress_display([], 'terms')
def test_agree(self):
r = self.client.post(self.url, {'read_dev_agreement': True})
self.assertRedirects(r, reverse('submit.app.manifest'))
#dt = self.get_user().read_dev_agreement
#assert close_to_now(dt), (
# 'Expected date of agreement read to be close to now. Was %s' % dt)
eq_(self.get_user().read_dev_agreement, True)
eq_(UserNotification.objects.count(), 0)
def test_agree_and_sign_me_up(self):
r = self.client.post(self.url, {'read_dev_agreement': True,
'newsletter': True})
self.assertRedirects(r, reverse('submit.app.manifest'))
#dt = self.get_user().read_dev_agreement
#assert close_to_now(dt), (
# 'Expected date of agreement read to be close to now. Was %s' % dt)
eq_(self.get_user().read_dev_agreement, True)
eq_(UserNotification.objects.count(), 1)
notes = UserNotification.objects.filter(user=self.user, enabled=True,
notification_id=app_surveys.id)
eq_(notes.count(), 1, 'Expected to not be subscribed to newsletter')
def test_disagree(self):
r = self.client.post(self.url)
eq_(r.status_code, 200)
eq_(self.user.read_dev_agreement, False)
eq_(UserNotification.objects.count(), 0)
class TestManifest(TestSubmit):
fixtures = ['base/users']
def setUp(self):
super(TestManifest, self).setUp()
self.user.update(read_dev_agreement=False)
self.url = reverse('submit.app.manifest')
def _step(self):
#self.user.update(read_dev_agreement=datetime.datetime.now())
self.user.update(read_dev_agreement=True)
def test_anonymous(self):
self._test_anonymous()
def test_cannot_skip_prior_step(self):
r = self.client.get(self.url, follow=True)
# And we start back at one...
self.assertRedirects(r, reverse('submit.app.terms'))
def test_jump_to_step(self):
# I already read the Terms.
self._step()
# So jump me to the Manifest step.
r = self.client.get(reverse('submit.app'), follow=True)
self.assertRedirects(r, reverse('submit.app.manifest'))
def test_page(self):
self._step()
r = self.client.get(self.url)
eq_(r.status_code, 200)
eq_(pq(r.content)('#submit-manifest').length, 1)
def test_progress_display(self):
self._step()
self._test_progress_display(['terms'], 'manifest')
class UploadAddon(object):
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 'form' in r.context:
eq_(r.context['form'].errors, {})
return r
class BaseWebAppTest(BaseUploadTest, UploadAddon, amo.tests.TestCase):
fixtures = ['base/apps', 'base/users', 'base/platforms']
def setUp(self):
super(BaseWebAppTest, self).setUp()
self.manifest = os.path.join(settings.ROOT, 'mkt', 'submit', 'tests',
'webapps', 'mozball.webapp')
self.manifest_url = 'http://allizom.org/mozball.webapp'
self.upload = self.get_upload(abspath=self.manifest)
self.upload.name = self.manifest_url
self.upload.save()
self.url = reverse('submit.app.manifest')
assert self.client.login(username='regular@mozilla.com',
password='password')
# Complete first step.
self.client.post(reverse('submit.app.terms'),
{'read_dev_agreement': True})
def post_addon(self):
eq_(Addon.objects.count(), 0)
self.post()
return Addon.objects.get()
class TestCreateWebApp(BaseWebAppTest):
def test_post_app_redirect(self):
r = self.post()
webapp = Webapp.objects.get()
self.assertRedirects(r,
reverse('submit.app.details', args=[webapp.app_slug]))
def test_no_hint(self):
self.post_addon()
self.upload = self.get_upload(abspath=self.manifest)
r = self.client.post(reverse('mkt.developers.upload_manifest'),
dict(manifest=self.manifest_url), follow=True)
eq_(r.status_code, 200)
assert 'already submitted' not in r.content, (
'Unexpected helpful error (trap_duplicate)')
assert 'already exists' not in r.content, (
'Unexpected validation error (verify_app_domain)')
def test_hint_for_same_manifest(self):
waffle.models.Switch.objects.create(name='webapps-unique-by-domain',
active=True)
self.post_addon()
self.upload = self.get_upload(abspath=self.manifest)
r = self.client.post(reverse('mkt.developers.upload_manifest'),
dict(manifest=self.manifest_url))
data = json.loads(r.content)
assert 'Oops' in data['validation']['messages'][0]['message'], (
'Expected oops')
def test_no_hint_for_same_manifest_different_author(self):
waffle.models.Switch.objects.create(name='webapps-unique-by-domain',
active=True)
self.post_addon()
# Submit same manifest as different user.
assert self.client.login(username='clouserw@gmail.com',
password='password')
self.upload = self.get_upload(abspath=self.manifest)
r = self.client.post(reverse('mkt.developers.upload_manifest'),
dict(manifest=self.manifest_url))
data = json.loads(r.content)
eq_(data['validation']['messages'][0]['message'],
'An app already exists on this domain; only one app per domain is '
'allowed.')
def test_app_from_uploaded_manifest(self):
addon = self.post_addon()
eq_(addon.type, amo.ADDON_WEBAPP)
eq_(addon.guid, None)
eq_(unicode(addon.name), u'MozillaBall ょ')
eq_(addon.slug, 'app-%s' % addon.id)
eq_(addon.app_slug, u'mozillaball-ょ')
eq_(addon.summary, u'Exciting Open Web development action!')
eq_(Translation.objects.get(id=addon.summary.id, locale='it'),
u'Azione aperta emozionante di sviluppo di fotoricettore!')
def test_manifest_with_any_extension(self):
self.manifest = os.path.join(settings.ROOT, 'mkt', 'developers',
'tests', 'addons', 'mozball.owa')
self.upload = self.get_upload(abspath=self.manifest, is_webapp=True)
addon = self.post_addon()
eq_(addon.type, amo.ADDON_WEBAPP)
def test_version_from_uploaded_manifest(self):
addon = self.post_addon()
eq_(addon.current_version.version, '1.0')
def test_file_from_uploaded_manifest(self):
addon = self.post_addon()
files = addon.current_version.files.all()
eq_(len(files), 1)
eq_(files[0].status, amo.STATUS_PUBLIC)
class TestCreateWebAppFromManifest(BaseWebAppTest):
def setUp(self):
super(TestCreateWebAppFromManifest, self).setUp()
Webapp.objects.create(app_slug='xxx',
app_domain='http://existing-app.com')
def upload_webapp(self, manifest_url, **post_kw):
self.upload.update(name=manifest_url) # Simulate JS upload.
return self.post(**post_kw)
def post_manifest(self, manifest_url):
rs = self.client.post(reverse('mkt.developers.upload_manifest'),
dict(manifest=manifest_url))
if 'json' in rs['content-type']:
rs = json.loads(rs.content)
return rs
def test_duplicate_domain(self):
waffle.models.Switch.objects.create(name='webapps-unique-by-domain',
active=True)
rs = self.upload_webapp('http://existing-app.com/my.webapp',
expect_errors=True)
eq_(rs.context['form'].errors,
{'upload':
['An app already exists on this domain; only one '
'app per domain is allowed.']})
def test_allow_duplicate_domains(self):
self.upload_webapp('http://existing-app.com/my.webapp') # No errors.
def test_duplicate_domain_from_js(self):
waffle.models.Switch.objects.create(name='webapps-unique-by-domain',
active=True)
data = self.post_manifest('http://existing-app.com/my.webapp')
eq_(data['validation']['errors'], 1)
eq_(data['validation']['messages'][0]['message'],
'An app already exists on this domain; '
'only one app per domain is allowed.')
def test_allow_duplicate_domains_from_js(self):
rs = self.post_manifest('http://existing-app.com/my.webapp')
eq_(rs.status_code, 302)
class TestDetails(TestSubmit):
fixtures = ['base/apps', 'base/users', 'webapps/337141-steamcube']
def setUp(self):
super(TestDetails, self).setUp()
self.webapp = self.get_webapp()
self.webapp.update(status=amo.STATUS_NULL)
self.url = reverse('submit.app.details', args=[self.webapp.app_slug])
def get_webapp(self):
return Webapp.objects.get(id=337141)
def upload_preview(self, image_file=None):
return self._upload_image(self.webapp.get_dev_url('upload_preview'),
image_file=image_file)
def upload_icon(self, image_file=None):
return self._upload_image(self.webapp.get_dev_url('upload_icon'),
image_file=image_file)
def _upload_image(self, url, image_file=None):
if not image_file:
image_file = get_image_path('non-animated.png')
with open(image_file, 'rb') as data:
rp = self.client.post(url, {'upload_image': data})
eq_(rp.status_code, 200)
return json.loads(rp.content)['upload_hash']
def _step(self):
#self.user.update(read_dev_agreement=datetime.datetime.now())
self.user.update(read_dev_agreement=True)
self.cl = AppSubmissionChecklist.objects.create(addon=self.webapp,
terms=True, manifest=True)
# Associate app with user.
AddonUser.objects.create(addon=self.webapp, user=self.user)
# Associate device type with app.
self.dtype = DeviceType.objects.create(name='fligphone')
AddonDeviceType.objects.create(addon=self.webapp,
device_type=self.dtype)
# Associate category with app.
self.cat1 = Category.objects.create(type=amo.ADDON_WEBAPP, name='Fun')
AddonCategory.objects.create(addon=self.webapp, category=self.cat1)
# Create initial formset for categories.
ctx = self.client.get(self.url).context['form_cats']
self.cat_initial = amo.tests.initial(ctx.initial_forms[0])
def test_anonymous(self):
self._test_anonymous()
def test_resume_step(self):
self._step()
payments_url = reverse('submit.app.payments',
args=[self.webapp.app_slug])
r = self.client.get(payments_url, follow=True)
self.assertRedirects(r, reverse('submit.app.details',
args=[self.webapp.app_slug]))
def test_resume_later(self):
self._step()
self.webapp.appsubmissionchecklist.update(details=True, payments=True)
self.webapp.update(status=amo.STATUS_NULL, paypal_id='',
premium_type=amo.ADDON_PREMIUM)
res = self.client.get(reverse('submit.app.resume',
args=[self.webapp.app_slug]))
self.assertRedirects(res, self.webapp.get_dev_url('paypal_setup'))
def test_not_owner(self):
self._step()
assert self.client.login(username='clouserw@gmail.com',
password='password')
eq_(self.client.get(self.url).status_code, 403)
def test_page(self):
self._step()
r = self.client.get(self.url)
eq_(r.status_code, 200)
eq_(pq(r.content)('#submit-details').length, 1)
def test_progress_display(self):
self._step()
self._test_progress_display(['terms', 'manifest'], 'details')
def new_preview_formset(self, *args, **kw):
ctx = self.client.get(self.url).context
blank = initial(ctx['form_previews'].forms[-1])
blank.update(**kw)
return blank
def preview_formset(self, *args, **kw):
kw.setdefault('initial_count', 0)
kw.setdefault('prefix', 'files')
fs = formset(*[a for a in args] + [self.new_preview_formset()], **kw)
return dict([(k, '' if v is None else v) for k, v in fs.items()])
def get_dict(self, **kw):
data = {
'name': 'Test name',
'slug': 'testname',
'summary': 'Hello!',
'description': 'desc',
'privacy_policy': 'XXX <script>alert("xss")</script>',
'homepage': 'http://www.goodreads.com/user/show/7595895-krupa',
'support_url': 'http://www.goodreads.com/user_challenges/351558',
'support_email': 'krupa+to+the+rescue@goodreads.com',
'device_types': [self.dtype.id],
}
# Add the required screenshot.
data.update(self.preview_formset({
'upload_hash': '<hash>',
'position': 0
}))
data.update(**kw)
# Build formset for categories.
data.update(amo.tests.formset(self.cat_initial, initial_count=1))
# Remove fields without values.
data = dict((k, v) for k, v in data.iteritems() if v is not None)
return data
def check_dict(self, data=None, expected=None):
if data is None:
data = self.get_dict()
addon = self.get_webapp()
# Build a dictionary of expected results.
expected = {
'name': 'Test name',
'app_slug': 'testname',
'summary': 'Hello!',
'description': 'desc',
'privacy_policy': 'XXX &lt;script&gt;alert("xss")&lt;/script&gt;',
}
expected.update(expected)
for field, expected in expected.iteritems():
got = unicode(getattr(addon, field))
eq_(got, expected,
'Expected %r for %r. Got %r.' % (expected, field, got))
eq_(list(addon.device_types), [self.dtype])
def test_success(self):
self._step()
data = self.get_dict()
# Post and be redirected.
r = self.client.post(self.url, data)
self.assertNoFormErrors(r)
# TODO: Assert redirects when we go to next step.
self.check_dict(data=data)
def test_no_video_types(self):
self._step()
res = self.client.get(self.url)
doc = pq(res.content)
eq_(doc('#screenshot_upload').attr('data-allowed-types'),
'image/jpeg|image/png')
eq_(doc('#id_icon_upload').attr('data-allowed-types'),
'image/jpeg|image/png')
def test_video_types(self):
waffle.models.Switch.objects.create(name='video-upload', active=True)
self._step()
res = self.client.get(self.url)
doc = pq(res.content)
eq_(doc('#screenshot_upload').attr('data-allowed-types'),
'image/jpeg|image/png|video/webm')
def test_screenshot(self):
self._step()
im_hash = self.upload_preview()
data = self.get_dict()
data.update(self.preview_formset({
'upload_hash': im_hash,
'position': 0
}))
rp = self.client.post(self.url, data)
eq_(rp.status_code, 302)
ad = Addon.objects.get(pk=self.webapp.pk)
eq_(ad.previews.all().count(), 1)
def test_icon(self):
self._step()
im_hash = self.upload_icon()
data = self.get_dict()
data['icon_upload_hash'] = im_hash
data['icon_type'] = 'image/png'
rp = self.client.post(self.url, data)
eq_(rp.status_code, 302)
ad = self.get_webapp()
eq_(ad.icon_type, 'image/png')
for size in amo.ADDON_ICON_SIZES:
fn = '%s-%s.png' % (ad.id, size)
assert os.path.exists(os.path.join(ad.get_icon_dir(), fn)), (
'Expected %s in %s' % (fn, os.listdir(ad.get_icon_dir())))
def _setup_other_webapp(self):
self._step()
# Generate another webapp to test name uniqueness.
app = amo.tests.addon_factory(type=amo.ADDON_WEBAPP, name='Cool App')
eq_(ReverseNameLookup(webapp=True).get(app.name), app.id)
def test_name_unique(self):
self._setup_other_webapp()
r = self.client.post(self.url, self.get_dict(name='Cool App'))
error = 'This name is already in use. Please choose another.'
self.assertFormError(r, 'form_basic', 'name', error)
def test_name_unique_strip(self):
# Make sure we can't sneak in a name by adding a space or two.
self._setup_other_webapp()
r = self.client.post(self.url, self.get_dict(name=' Cool App '))
error = 'This name is already in use. Please choose another.'
self.assertFormError(r, 'form_basic', 'name', error)
def test_name_unique_case(self):
# Make sure unique names aren't case sensitive.
self._setup_other_webapp()
r = self.client.post(self.url, self.get_dict(name='cool app'))
error = 'This name is already in use. Please choose another.'
self.assertFormError(r, 'form_basic', 'name', error)
def test_name_required(self):
self._step()
r = self.client.post(self.url, self.get_dict(name=''))
eq_(r.status_code, 200)
self.assertFormError(r, 'form_basic', 'name',
'This field is required.')
def test_screenshot_required(self):
self._step()
data = self.get_dict()
for k in data:
if k.startswith('files') and k.endswith('upload_hash'):
data[k] = ''
rp = self.client.post(self.url, data)
eq_(rp.context['form_previews'].non_form_errors(),
['You must upload at least one screenshot.'])
def test_screenshot_or_video_required(self):
waffle.models.Switch.objects.create(name='video-upload', active=True)
self._step()
data = self.get_dict()
for k in data:
if k.startswith('files') and k.endswith('upload_hash'):
data[k] = ''
rp = self.client.post(self.url, data)
eq_(rp.context['form_previews'].non_form_errors(),
['You must upload at least one screenshot or video.'])
def test_unsaved_screenshot(self):
self._step()
# If there are form errors we should still pass the previews URIs.
preview_type = 'video/webm'
preview_uri = 'moz-filedata:p00p'
data = self.preview_formset({
'position': 1,
'upload_hash': '<hash_one>',
'unsaved_image_type': preview_type,
'unsaved_image_data': preview_uri
})
r = self.client.post(self.url, data)
eq_(r.status_code, 200)
form = pq(r.content)('form')
eq_(form.find('input[name=files-0-unsaved_image_type]').val(),
preview_type)
eq_(form.find('input[name=files-0-unsaved_image_data]').val(),
preview_uri)
def test_name_length(self):
self._step()
r = self.client.post(self.url, self.get_dict(name='a' * 129))
eq_(r.status_code, 200)
self.assertFormError(r, 'form_basic', 'name',
'Ensure this value has at most 128 characters (it has 129).')
def test_slug_invalid(self):
self._step()
# Submit an invalid slug.
d = self.get_dict(slug='slug!!! aksl23%%')
r = self.client.post(self.url, d)
eq_(r.status_code, 200)
self.assertFormError(r, 'form_basic', 'slug',
"Enter a valid 'slug' consisting of letters, numbers, underscores "
"or hyphens.")
def test_slug_required(self):
self._step()
r = self.client.post(self.url, self.get_dict(slug=''))
eq_(r.status_code, 200)
self.assertFormError(r, 'form_basic', 'slug',
'This field is required.')
def test_summary_required(self):
self._step()
r = self.client.post(self.url, self.get_dict(summary=''))
eq_(r.status_code, 200)
self.assertFormError(r, 'form_basic', 'summary',
'This field is required.')
def test_summary_length(self):
self._step()
r = self.client.post(self.url, self.get_dict(summary='a' * 251))
eq_(r.status_code, 200)
self.assertFormError(r, 'form_basic', 'summary',
'Ensure this value has at most 250 characters (it has 251).')
def test_description_optional(self):
self._step()
r = self.client.post(self.url, self.get_dict(description=None))
self.assertNoFormErrors(r)
def test_privacy_policy_required(self):
self._step()
r = self.client.post(self.url, self.get_dict(privacy_policy=None))
self.assertFormError(r, 'form_basic', 'privacy_policy',
'This field is required.')
def test_clashing_locale(self):
self.webapp.default_locale = 'de'
self.webapp.save()
self._step()
self.client.cookies['current_locale'] = 'en-us'
data = self.get_dict(name=None, name_de='Test name',
privacy_policy=None,
**{'privacy_policy_en-us': 'XXX'})
r = self.client.post(self.url, data)
self.assertNoFormErrors(r)
def test_homepage_url_optional(self):
self._step()
r = self.client.post(self.url, self.get_dict(homepage=None))
self.assertNoFormErrors(r)
def test_homepage_url_invalid(self):
self._step()
r = self.client.post(self.url, self.get_dict(homepage='xxx'))
self.assertFormError(r, 'form_basic', 'homepage', 'Enter a valid URL.')
def test_support_url_optional(self):
self._step()
r = self.client.post(self.url, self.get_dict(support_url=None))
self.assertNoFormErrors(r)
def test_support_url_invalid(self):
self._step()
r = self.client.post(self.url, self.get_dict(support_url='xxx'))
self.assertFormError(r, 'form_basic', 'support_url',
'Enter a valid URL.')
def test_support_email_required(self):
self._step()
r = self.client.post(self.url, self.get_dict(support_email=None))
self.assertFormError(r, 'form_basic', 'support_email',
'This field is required.')
def test_support_email_invalid(self):
self._step()
r = self.client.post(self.url, self.get_dict(support_email='xxx'))
self.assertFormError(r, 'form_basic', 'support_email',
'Enter a valid e-mail address.')
def test_device_types_required(self):
self._step()
r = self.client.post(self.url, self.get_dict(device_types=None))
self.assertFormError(r, 'form_devices', 'device_types',
'This field is required.')
def test_device_types_invalid(self):
self._step()
r = self.client.post(self.url, self.get_dict(device_types='999'))
self.assertFormError(r, 'form_devices', 'device_types',
'Select a valid choice. 999 is not one of the available choices.')
def test_device_types_default(self):
self._step()
r = self.client.get(self.url)
eq_(r.status_code, 200)
checkboxes = pq(r.content)('input[name=device_types]')
eq_(checkboxes.length, 1)
eq_(checkboxes.filter(':checked').length, 1,
'All device types should be checked by default.')
def test_device_types_default_on_post(self):
self._step()
r = self.client.post(self.url, self.get_dict(device_types=None))
eq_(r.status_code, 200)
checkboxes = pq(r.content)('input[name=device_types]')
eq_(checkboxes.length, 1)
eq_(checkboxes.filter(':checked').length, 0,
'POSTed values should not get replaced by the defaults.')
def test_categories_required(self):
self._step()
del self.cat_initial['categories']
r = self.client.post(self.url, self.get_dict())
eq_(r.context['form_cats'].errors[0]['categories'],
['This field is required.'])
def test_categories_max(self):
self._step()
eq_(amo.MAX_CATEGORIES, 2)
cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
cat3 = Category.objects.create(type=amo.ADDON_WEBAPP, name='blang')
self.cat_initial['categories'] = [self.cat1.id, cat2.id, cat3.id]
r = self.client.post(self.url, self.get_dict())
eq_(r.context['form_cats'].errors[0]['categories'],
['You can have only 2 categories.'])
def _post_cats(self, cats):
self.cat_initial['categories'] = [c.id for c in cats]
self.client.post(self.url, self.get_dict())
eq_(sorted(self.get_webapp().categories.values_list('id', flat=True)),
sorted(c.id for c in cats))
def test_categories_add(self):
self._step()
cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
self._post_cats([self.cat1, cat2])
def test_categories_add_and_remove(self):
self._step()
cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
self._post_cats([cat2])
def test_categories_remove(self):
# Add another category here so it gets added to the initial formset.
cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
AddonCategory.objects.create(addon=self.webapp, category=cat2)
self._step()
# `cat2` should get removed.
self._post_cats([self.cat1])
class TestPayments(TestSubmit):
fixtures = ['base/users', 'webapps/337141-steamcube']
def setUp(self):
super(TestPayments, self).setUp()
self.webapp = self.get_webapp()
self.webapp.update(status=amo.STATUS_NULL)
self.url = self.get_url('payments')
self.price = Price.objects.create(price='1.00')
self._step()
def get_url(self, url):
return reverse('submit.app.%s' % url, args=[self.webapp.app_slug])
def get_webapp(self):
return Webapp.objects.get(id=337141)
def _step(self):
self.cl = AppSubmissionChecklist.objects.create(addon=self.webapp,
terms=True, manifest=True, details=True)
AddonUser.objects.create(addon=self.webapp, user=self.user)
def test_anonymous(self):
self._test_anonymous()
def test_required(self):
res = self.client.post(self.url, {'premium_type': ''})
eq_(res.status_code, 200)
self.assertFormError(res, 'form', 'premium_type',
'This field is required.')
def test_premium_type_not_valid(self):
res = self.client.post(self.url, {'premium_type': 124})
eq_(res.status_code, 200)
self.assertFormError(res, 'form', 'premium_type',
'Select a valid choice. 124 is not one of the available choices.')
def test_premium_type_valid(self):
res = self.client.post(self.url, {'premium_type': amo.ADDON_PREMIUM})
eq_(res.status_code, 302)
eq_(self.get_webapp().premium_type, amo.ADDON_PREMIUM)
def _test_valid(self, expected_status):
res = self.client.post(self.get_url('payments'),
{'premium_type': amo.ADDON_FREE})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('done'))
eq_(self.get_webapp().status, expected_status)
def test_valid_pending(self):
res = self.client.post(self.get_url('payments'),
{'premium_type': amo.ADDON_FREE})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('done'))
eq_(self.get_webapp().status, amo.WEBAPPS_UNREVIEWED_STATUS)
def test_premium(self):
for type_ in [amo.ADDON_PREMIUM, amo.ADDON_PREMIUM_INAPP]:
res = self.client.post(self.get_url('payments'),
{'premium_type': type_})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('payments.upsell'))
def test_free_inapp(self):
res = self.client.post(self.get_url('payments'),
{'premium_type': amo.ADDON_FREE_INAPP})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('payments.paypal'))
def test_premium_other(self):
res = self.client.post(self.get_url('payments'),
{'premium_type': amo.ADDON_PREMIUM_OTHER})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('done'))
def test_price(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.upsell'),
{'price': self.price.pk})
eq_(res.status_code, 302)
eq_(self.get_webapp().premium.price.pk, self.price.pk)
self.assertRedirects(res, self.get_url('payments.paypal'))
def _make_upsell(self):
free = Addon.objects.create(type=amo.ADDON_WEBAPP)
free.update(status=amo.STATUS_PUBLIC)
AddonUser.objects.create(addon=free, user=self.user)
return free
def test_immediate(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.upsell'),
{'price': self.price.pk,
'make_public': 0})
eq_(res.status_code, 302)
eq_(self.get_webapp().make_public, amo.PUBLIC_IMMEDIATELY)
def test_wait(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.upsell'),
{'price': self.price.pk,
'make_public': 1})
eq_(res.status_code, 302)
eq_(self.get_webapp().make_public, amo.PUBLIC_WAIT)
def test_upsell_states(self):
free = self._make_upsell()
free.update(status=amo.STATUS_NULL)
res = self.client.get(self.get_url('payments.upsell'))
eq_(len(res.context['form'].fields['free'].choices), 0)
def test_upsell_states_inapp(self):
free = self._make_upsell()
free.update(premium_type=amo.ADDON_FREE_INAPP)
res = self.client.get(self.get_url('payments.upsell'))
eq_(len(res.context['form'].fields['free'].choices), 1)
def test_upsell(self):
free = self._make_upsell()
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.upsell'),
{'price': self.price.pk,
'do_upsell': 1,
'free': free.pk,
'text': 'some upsell',
})
eq_(self.get_webapp().premium.price.pk, self.price.pk)
eq_(self.get_webapp().upsold.free.pk, free.pk)
eq_(self.get_webapp().upsold.premium.pk, self.get_webapp().pk)
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('payments.paypal'))
def test_no_upsell(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.get(self.get_url('payments.upsell'),
{'price': self.price.pk})
eq_(res.status_code, 200)
eq_(len(pq(res.content)('div.brform')), 3)
def test_upsell_missing(self):
free = Addon.objects.create(type=amo.ADDON_WEBAPP)
AddonUser.objects.create(addon=free, user=self.user)
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.upsell'),
{'price': self.price.pk,
'do_upsell': 1,
})
eq_(res.status_code, 200)
def test_bad_upsell(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.upsell'), {'price': ''})
eq_(res.status_code, 200)
self.assertFormError(res, 'form', 'price', 'This field is required.')
def test_paypal(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.paypal'),
{'business_account': 'yes',
'email': 'foo@bar.com'})
eq_(self.get_webapp().paypal_id, 'foo@bar.com')
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('payments.bounce'))
@mock.patch('mkt.submit.views.client')
@mock.patch('mkt.submit.views.waffle.flag_is_active')
def test_paypal_solitude(self, flag_is_active, client):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.paypal'),
{'business_account': 'yes',
'email': 'foo@bar.com'})
eq_(client.create_seller_paypal.call_args[0][0], self.webapp)
eq_(client.patch_seller_paypal.call_args[1]['data']['paypal_id'],
'foo@bar.com')
self.assertRedirects(res, self.get_url('payments.bounce'))
def test_no_paypal(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.paypal'),
{'business_account': 'no'})
eq_(res.status_code, 302)
eq_(res._headers['location'][1], settings.PAYPAL_CGI_URL)
def test_later_paypal(self):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
res = self.client.post(self.get_url('payments.paypal'),
{'business_account': 'later'})
eq_(res.status_code, 302)
self.assertRedirects(res, self.get_url('done'))
def get_acquire_url(self):
url = self.webapp.get_dev_url('acquire_refund_permission')
return urlparams(url, dest='submission', request_token='foo',
verification_code='foo')
@mock.patch('paypal.get_permissions_token')
@mock.patch('paypal.get_personal_data')
def test_bounce_result_works(self, get_personal_data,
get_permissions_token):
self.webapp.update(premium_type=amo.ADDON_PREMIUM,
paypal_id='a@a.com')
get_permissions_token.return_value = 'foo'
get_personal_data.return_value = {'email': 'a@a.com'}
res = self.client.get(self.get_acquire_url())
self.assertRedirects(res, self.get_url('payments.confirm'))
@mock.patch('paypal.get_permissions_token')
@mock.patch('paypal.get_personal_data')
def test_bounce_result_fails_email(self, get_personal_data,
get_permissions_token):
self.webapp.update(premium_type=amo.ADDON_PREMIUM,
paypal_id='b@b.com')
get_permissions_token.return_value = 'foo'
get_personal_data.return_value = {'email': 'a@a.com'}
res = self.client.get(self.get_acquire_url())
self.assertRedirects(res, self.get_url('payments.paypal'))
@mock.patch('paypal.get_permissions_token')
def test_bounce_result_fails_paypal_error(self, get_permissions_token):
self.webapp.update(premium_type=amo.ADDON_PREMIUM)
get_permissions_token.side_effect = paypal.PaypalError
res = self.client.get(self.get_acquire_url())
eq_(res.status_code, 500)
self.assertTemplateUsed(res, 'site/500_paypal.html')
doc = pq(res.content)
eq_(doc('div.prose form a').attr('href'),
self.get_url('payments.bounce'))
eq_(doc('div.prose form').attr('action'),
self.get_url('payments.paypal'))
class TestDone(TestSubmit):
fixtures = ['base/users', 'webapps/337141-steamcube']
def setUp(self):
super(TestDone, self).setUp()
self.webapp = self.get_webapp()
self.url = reverse('submit.app.done', args=[self.webapp.app_slug])
def get_webapp(self):
return Webapp.objects.get(id=337141)
def _step(self):
self.cl = AppSubmissionChecklist.objects.create(addon=self.webapp,
terms=True, manifest=True, details=True, payments=True)
AddonUser.objects.create(addon=self.webapp, user=self.user)
def test_anonymous(self):
self._test_anonymous()
def test_done(self):
self._step()
res = self.client.get(self.url)
eq_(res.status_code, 200)