From ab3cd22569ecefa11d01cb331d8135f5ead783df Mon Sep 17 00:00:00 2001 From: Paul Marillonnet Date: Tue, 4 Sep 2018 16:26:15 +0200 Subject: [PATCH] support avatar picture in user profile (#26022) --- debian-wheezy/control | 3 +- debian/control | 3 +- setup.py | 2 + src/authentic2/app_settings.py | 3 + src/authentic2/attribute_kinds.py | 29 +++ src/authentic2/forms/widgets.py | 22 ++- src/authentic2/models.py | 19 +- src/authentic2/settings.py | 3 + .../templates/authentic2/accounts.html | 20 ++- .../templates/authentic2/accounts_edit.html | 3 +- .../templates/authentic2/accounts_image.html | 6 + .../registration/registration_form.html | 3 +- src/authentic2/templatetags/__init__.py | 0 src/authentic2/templatetags/authentic2.py | 20 +++ src/authentic2/urls.py | 5 + src/authentic2/utils.py | 63 +++++++ tests/cityscape.png | Bin 0 -> 508 bytes tests/conftest.py | 19 ++ tests/garden.png | Bin 0 -> 506 bytes tests/test_attribute_kinds.py | 168 ++++++++++++++++++ tests/test_manager.py | 23 +-- tests/test_profile.py | 16 +- tox.ini | 2 + 23 files changed, 401 insertions(+), 31 deletions(-) create mode 100644 src/authentic2/templates/authentic2/accounts_image.html create mode 100644 src/authentic2/templatetags/__init__.py create mode 100644 src/authentic2/templatetags/authentic2.py create mode 100644 tests/cityscape.png create mode 100644 tests/garden.png diff --git a/debian-wheezy/control b/debian-wheezy/control index b8039228..3f027bfa 100644 --- a/debian-wheezy/control +++ b/debian-wheezy/control @@ -25,7 +25,8 @@ Depends: ${misc:Depends}, ${python:Depends}, python-markdown (>= 2.1), python-ldap (>= 2.4), python-six (>= 1.0), - python-django-filters (>= 1) + python-django-filters (>= 1), + python-sorl-thumbnail Provides: ${python:Provides} Recommends: python-ldap Suggests: python-raven diff --git a/debian/control b/debian/control index 1a419007..e3db3264 100644 --- a/debian/control +++ b/debian/control @@ -28,7 +28,8 @@ Depends: ${misc:Depends}, ${python:Depends}, python-jwcrypto (>= 0.3.1), python-cryptography (>= 1.3.4), python-django-filters (>= 1), - python-django-filters (<< 2) + python-django-filters (<< 2), + python-sorl-thumbnail Provides: ${python:Provides} Recommends: python-ldap Suggests: python-raven diff --git a/setup.py b/setup.py index 241aa73d..2707fee3 100755 --- a/setup.py +++ b/setup.py @@ -131,6 +131,8 @@ setup(name="authentic2", 'XStatic-jQuery', 'XStatic-jquery-ui<1.12', 'xstatic-select2', + 'sorl-thumbnail', + 'pillow', ], zip_safe=False, classifiers=[ diff --git a/src/authentic2/app_settings.py b/src/authentic2/app_settings.py index 19154118..57888954 100644 --- a/src/authentic2/app_settings.py +++ b/src/authentic2/app_settings.py @@ -176,6 +176,9 @@ default_settings = dict( 'next try after a login failure'), A2_VERIFY_SSL=Setting(default=True, definition='Verify SSL certificate in HTTP requests'), A2_ATTRIBUTE_KIND_TITLE_CHOICES=Setting(default=(), definition='Choices for the title attribute kind'), + A2_ATTRIBUTE_KIND_IMAGE_DIMENSIONS=Setting(default="", definition='Width x Height image dimensions in account management page.'), + A2_ATTRIBUTE_KIND_IMAGE_CROPPING=Setting(default="", definition='Image cropping in account management page.'), + A2_ATTRIBUTE_KIND_IMAGE_QUALITY=Setting(default=None, definition='Image quality in account management page.'), A2_CORS_WHITELIST=Setting(default=(), definition='List of origin URL to whitelist, must be scheme://netloc[:port]'), A2_EMAIL_CHANGE_TOKEN_LIFETIME=Setting(default=7200, definition='Lifetime in seconds of the ' 'token sent to verify email adresses'), diff --git a/src/authentic2/attribute_kinds.py b/src/authentic2/attribute_kinds.py index 27ae00b4..6ee83f1b 100644 --- a/src/authentic2/attribute_kinds.py +++ b/src/authentic2/attribute_kinds.py @@ -17,6 +17,7 @@ from .decorators import to_iter from .plugins import collect_from_plugins from . import app_settings from .forms import widgets +from .utils import image_serialize, image_deserialize capfirst = allow_lazy(capfirst, unicode) @@ -25,6 +26,9 @@ DEFAULT_TITLE_CHOICES = ( (pgettext_lazy('title', 'Mr'), pgettext_lazy('title', 'Mr')), ) +DEFAULT_IMAGE_DIMENSIONS = "150x150" +DEFAULT_IMAGE_CROPPING = "center" +DEFAULT_IMAGE_QUALITY = 99 class BirthdateWidget(widgets.DateWidget): help_text = _('Format: yyyy-mm-dd') @@ -49,6 +53,13 @@ class BirthdateField(forms.DateField): def get_title_choices(): return app_settings.A2_ATTRIBUTE_KIND_TITLE_CHOICES or DEFAULT_TITLE_CHOICES + +def get_image_thumbnail_parameters(): + return (app_settings.A2_ATTRIBUTE_KIND_IMAGE_DIMENSIONS or DEFAULT_IMAGE_DIMENSIONS, + app_settings.A2_ATTRIBUTE_KIND_IMAGE_CROPPING or DEFAULT_IMAGE_CROPPING, + app_settings.A2_ATTRIBUTE_KIND_IMAGE_QUALITY or DEFAULT_IMAGE_QUALITY) + + validate_phone_number = RegexValidator('^\+?\d{,20}$', message=_('Phone number can start with a + ' 'an must contain only digits.')) @@ -151,6 +162,24 @@ DEFAULT_ATTRIBUTE_KINDS = [ 'field_class': PhoneNumberField, 'rest_framework_field_class': PhoneNumberDRFField, }, + { + 'label': _('image'), + 'name': 'image', + 'field_class': forms.ImageField, + 'kwargs': { + 'widget': widgets.AttributeImageInput, + }, + 'serialize': image_serialize, + 'serialize_eval_kwargs': { + 'owner_uuid': 'owner.uuid', + 'owner_pk': 'owner.pk', + 'attr_label': 'self.label', + }, + 'deserialize': image_deserialize, + 'rest_framework_field_kwargs': { + 'read_only': True, + }, + }, ] diff --git a/src/authentic2/forms/widgets.py b/src/authentic2/forms/widgets.py index c3d1dda2..9e69625a 100644 --- a/src/authentic2/forms/widgets.py +++ b/src/authentic2/forms/widgets.py @@ -11,7 +11,8 @@ import json import re import uuid -from django.forms.widgets import DateTimeInput, DateInput, TimeInput +from django.forms.widgets import DateTimeInput, DateInput, TimeInput, \ + ClearableFileInput from django.forms.widgets import PasswordInput as BasePasswordInput from django.utils.formats import get_language, get_format from django.utils.safestring import mark_safe @@ -246,3 +247,22 @@ class CheckPasswordInput(PasswordInput): json.dumps(_id), ) return output + + +class AttributeImageInput(ClearableFileInput): + template_name = "authentic2/accounts_image.html" + # Deprecated (django<=1.8 only): + template_with_initial = ( + '%(initial_text)s:

' + '%(clear_template)s
%(input_text)s: %(input)s' + ) + + def is_initial(self, value): + return bool(value) + + def get_template_substitution_values(self, value): + subs_values = dict() + subs_values.update({ + 'initial': value, + }) + return subs_values diff --git a/src/authentic2/models.py b/src/authentic2/models.py index b14f3085..77036a1d 100644 --- a/src/authentic2/models.py +++ b/src/authentic2/models.py @@ -1,3 +1,4 @@ +import logging import time import urlparse import uuid @@ -203,6 +204,7 @@ class Attribute(models.Model): return kind['default'] def set_value(self, owner, value, verified=False): + logger = logging.getLogger(__name__) serialize = self.get_kind()['serialize'] # setting to None is to delete if value is None: @@ -225,7 +227,22 @@ class Attribute(models.Model): av.verified = verified av.save() else: - content = serialize(value) + kwargs = dict() + for key, flat_value in self.get_kind().get('serialize_eval_kwargs', {}).items(): + try: + evalue = eval(flat_value) + except NameError: + logger.error("Couldn't evaluate {} for attribute <{}: {}>".format( + flat_value, + self.get_kind()['kind'], + self.label)) + continue + kwargs.update({key: evalue}) + if kwargs: + content = serialize(value, **kwargs) + else: + content = serialize(value) + av, created = AttributeValue.objects.get_or_create( content_type=ContentType.objects.get_for_model(owner), object_id=owner.pk, diff --git a/src/authentic2/settings.py b/src/authentic2/settings.py index c045ce2a..bf0378cb 100644 --- a/src/authentic2/settings.py +++ b/src/authentic2/settings.py @@ -22,6 +22,8 @@ SECRET_KEY = 'please-change-me-with-a-very-long-random-string' DEBUG = False DEBUG_DB = False MEDIA = 'media' +MEDIA_ROOT = 'media' +MEDIA_URL = '/media/' # See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts ALLOWED_HOSTS = [] @@ -132,6 +134,7 @@ INSTALLED_APPS = ( 'xstatic.pkg.jquery', 'xstatic.pkg.jquery_ui', 'xstatic.pkg.select2', + 'sorl.thumbnail', ) INSTALLED_APPS = tuple(plugins.register_plugins_installed_apps(INSTALLED_APPS)) diff --git a/src/authentic2/templates/authentic2/accounts.html b/src/authentic2/templates/authentic2/accounts.html index 4e9f979f..5e4bc487 100644 --- a/src/authentic2/templates/authentic2/accounts.html +++ b/src/authentic2/templates/authentic2/accounts.html @@ -1,5 +1,6 @@ {% extends "authentic2/base-page.html" %} {% load i18n %} +{% load authentic2 %} {% block page-title %} {{ block.super }} - {{ view.title }} @@ -18,14 +19,19 @@ {% for attribute in attributes %}
{{ attribute.attribute.label|capfirst }} :
- {% if attribute.values|length == 1 %} - {{ attribute.values.0 }} + {% if attribute.attribute.kind == 'image' %} + {% thumbnail attribute.values.0 as thumb_im %} + {% else %} - + {% if attribute.values|length == 1 %} + {{ attribute.values.0 }} + {% else %} + + {% endif %} {% endif %}
{% endfor %} diff --git a/src/authentic2/templates/authentic2/accounts_edit.html b/src/authentic2/templates/authentic2/accounts_edit.html index c7587b14..97a03324 100644 --- a/src/authentic2/templates/authentic2/accounts_edit.html +++ b/src/authentic2/templates/authentic2/accounts_edit.html @@ -12,7 +12,8 @@ {% endblock %} {% block content %} -
+ + {% csrf_token %} {{ form.as_p }} {% if form.instance and form.instance.id %} diff --git a/src/authentic2/templates/authentic2/accounts_image.html b/src/authentic2/templates/authentic2/accounts_image.html new file mode 100644 index 00000000..61a69fec --- /dev/null +++ b/src/authentic2/templates/authentic2/accounts_image.html @@ -0,0 +1,6 @@ +{% if widget.is_initial %}{{ widget.initial_text }}:
+{% if not widget.required %} + +{% endif %} +{{ widget.input_text }}:{% endif %} + diff --git a/src/authentic2/templates/registration/registration_form.html b/src/authentic2/templates/registration/registration_form.html index 292cf023..68252c9b 100644 --- a/src/authentic2/templates/registration/registration_form.html +++ b/src/authentic2/templates/registration/registration_form.html @@ -15,7 +15,8 @@

{{ view.title }}

- + + {% csrf_token %} {{ form.as_p }} diff --git a/src/authentic2/templatetags/__init__.py b/src/authentic2/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/authentic2/templatetags/authentic2.py b/src/authentic2/templatetags/authentic2.py new file mode 100644 index 00000000..a7029b9d --- /dev/null +++ b/src/authentic2/templatetags/authentic2.py @@ -0,0 +1,20 @@ +import logging + +from django import template +from sorl.thumbnail import get_thumbnail +from ..attribute_kinds import get_image_thumbnail_parameters + +register = template.Library() + + +@register.assignment_tag(takes_context=True) +def thumbnail(context, img_value): + logger = logging.getLogger(__name__) + + try: + img_url = context['request'].build_absolute_uri(img_value) + dimensions, crop, quality = get_image_thumbnail_parameters() + return get_thumbnail(img_url, dimensions, crop=crop, quality=quality) + except: + logger.error("Couldn't generate thumbnail for image {}".format( + img_value)) diff --git a/src/authentic2/urls.py b/src/authentic2/urls.py index 35b13139..b35b1559 100644 --- a/src/authentic2/urls.py +++ b/src/authentic2/urls.py @@ -2,6 +2,7 @@ from django.conf.urls import url, include from django.conf import settings from django.contrib import admin from django.contrib.staticfiles.views import serve +from django.views.static import serve as media_serve from . import app_settings, plugins, views @@ -44,6 +45,10 @@ if settings.DEBUG: urlpatterns += [ url(r'^static/(?P.*)$', serve) ] + urlpatterns += [ + url(r'^media/(?P.*)$', media_serve, { + 'document_root': settings.MEDIA_ROOT}) + ] if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS: import debug_toolbar diff --git a/src/authentic2/utils.py b/src/authentic2/utils.py index d32a5a67..2d2c5c00 100644 --- a/src/authentic2/utils.py +++ b/src/authentic2/utils.py @@ -8,6 +8,7 @@ import urlparse import uuid import datetime import copy +import os from functools import wraps from itertools import islice, chain, count @@ -30,6 +31,7 @@ from django.shortcuts import resolve_url from django.template.loader import render_to_string, TemplateDoesNotExist from django.core.mail import send_mail from django.core import signing +from django.core.files.storage import default_storage from django.core.urlresolvers import reverse, NoReverseMatch from django.utils.formats import localize from django.contrib import messages @@ -1073,3 +1075,64 @@ def get_user_flag(user, name, default=None): if ou_value is not None: return ou_value return default + + +def _store_image(in_memory_image, owner_uuid, attr_label): + img_tmp_path = u'{label}/{uuid_short}/{uuid}_{name}'.format( + label=attr_label, + uuid_short=owner_uuid[0:4], + uuid=owner_uuid, + name=in_memory_image.name) + + img_media_path = default_storage.save(img_tmp_path, in_memory_image) + + return img_media_path + + +def _delete_images_from_user(owner_pk, attr_label): + from .models import Attribute, AttributeValue + + logger = logging.getLogger(__name__) + User = get_user_model() + + try: + owner = User.objects.get(pk=owner_pk) + except User.DoesNotExist: + logger.error("Primary key {} doesn't match with any user.".format(owner_pk)) + return + + try: + attr = Attribute.objects.get(label=attr_label) + all_values = AttributeValue.objects.with_owner(owner) + values = all_values.filter(attribute=attr) + except: + logger.error("Couldn't retrieve values for Attribute {}.".format(attr_label)) + return + + for value in values: + if value.content: + # Direct URI <-> file location correspondence + local_file = value.content.split(default_storage.base_url)[-1] + default_storage.delete(local_file) + value.delete() + + +def image_serialize(image, owner_uuid, owner_pk, attr_label): + uri = '' + if isinstance(image, basestring): + uri = image + else: + # Discard previous user avatars + _delete_images_from_user(owner_pk, attr_label) + if image: + img_media_path = _store_image(image, owner_uuid, attr_label) + uri = os.path.join(settings.MEDIA_URL, img_media_path) + return uri + + +def image_deserialize(image_uri): + from authentic2.middleware import StoreRequestMiddleware + + if image_uri: + request = StoreRequestMiddleware().get_request() + return request.build_absolute_uri(image_uri) diff --git a/tests/cityscape.png b/tests/cityscape.png new file mode 100644 index 0000000000000000000000000000000000000000..b0dd7d00678798591b018adb2b5defbe020c8ffe GIT binary patch literal 508 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k2}mkgS)K$^oCO|{#S9F3${@^GvDCf{D9B#o z>Fdh=h*OwdS)}rY{RW_rWQl7;iF1B#Zfaf$gL6@8Vo7R>LV0FMhJw4NZ$Nk>pEv^p zW0|LmV@SoVx0ehB84NfWHU`byalnep;NXD?9zTA+IK_T@ABzNca-xob*s((q2|7Kk z8xKSP1sXR31(<;1J*+@+32vacffyYX4kovNgMp-Qi7^)6%j`R?^1x+aG%Fdh=h*OwdN&M%jcyFMPWQl7;iF1B#Zfaf$gL6@8Vo7R>LV0FMhJw4NZ$Nk>pEv^p zV~MAWV@SoVx0f6R85md?9FK&Dv8vkGlquhd`Q~$A>m-KcM4cYiV}~LnxO-YR8i+}7 z0|gAkfC33RK!F25@#IFJ_%S9rDjZC10S5yq;nJNic5){3k6pc%dB8|w@O1TaS?83{ F1OPy1s8j#| literal 0 HcmV?d00001 diff --git a/tests/test_attribute_kinds.py b/tests/test_attribute_kinds.py index b6ecf052..d8626099 100644 --- a/tests/test_attribute_kinds.py +++ b/tests/test_attribute_kinds.py @@ -279,3 +279,171 @@ def test_phone_number(db, app, admin, mailoutbox): app.post_json('/api/users/', params=payload) assert qs.get().attributes.phone_number == '' qs.delete() + + +def test_image(db, app, admin, mailoutbox, monkeypatch, monkeyrequest): + from webtest import Upload + from authentic2.middleware import StoreRequestMiddleware + + Attribute.objects.create(name='cityscape_image', label='cityscape', kind='image', + asked_on_registration=True) + qs = User.objects.filter(first_name='John') + + + response = app.get('/accounts/register/') + form = response.form + form.set('email', 'john.doe@example.com') + response = form.submit().follow() + assert 'john.doe@example.com' in response + url = get_link_from_mail(mailoutbox[0]) + response = app.get(url) + + form = response.form + form.set('first_name', 'John') + form.set('last_name', 'Doe') + form.set('cityscape_image', Upload('/dev/null')) + form.set('password1', '12345abcdA') + form.set('password2', '12345abcdA') + response = form.submit() + assert response.pyquery.find('.form-field-error #id_cityscape_image') + + form = response.form + form.set('cityscape_image', Upload('tests/cityscape.png')) + form.set('password1', '12345abcdA') + form.set('password2', '12345abcdA') + response = form.submit() + + john = qs.get() + monkeypatch.setattr(StoreRequestMiddleware, 'get_request', lambda cls: monkeyrequest) + assert john.uuid in john.attributes.cityscape_image + monkeypatch.undo() + + app.authorization = ('Basic', (admin.username, admin.username)) + + resp = app.get('/api/users/?first_name=John&last_name=Doe') + assert john.uuid in resp.json_body['results'][0]['cityscape_image'] + + qs.delete() + + response = app.get(url) + form = response.form + form.set('first_name', 'John') + form.set('last_name', 'Doe') + form.set('cityscape_image', None) + form.set('password1', '12345abcdA') + form.set('password2', '12345abcdA') + response = form.submit().follow() + assert qs.get().attributes.cityscape_image == None + qs.delete() + +def test_images_delete_on_form_field_clearance(db, app, admin, mailoutbox): + from django.core.files.storage import default_storage + from webtest import Upload + + Attribute.objects.create( + name='cityscape_image', label='cityscape', kind='image', + asked_on_registration=False, required=False, + user_visible=True, user_editable=True) + Attribute.objects.create( + name='garden_image', label='garden', kind='image', + asked_on_registration=False, required=False, + user_visible=True, user_editable=True) + + qs = User.objects.filter(first_name='John') + + response = app.get('/accounts/register/') + form = response.form + form.set('email', 'john.doe@example.com') + response = form.submit().follow() + assert 'john.doe@example.com' in response + url = get_link_from_mail(mailoutbox[0]) + response = app.get(url) + + form = response.form + form.set('first_name', 'John') + form.set('last_name', 'Doe') + form.set('password1', '12345abcdA') + form.set('password2', '12345abcdA') + response = form.submit() + + john = qs.get() + assert john + img_path1 = 'cityscape/{uuid_short}/{uuid}_cityscape.png'.format( + uuid_short=john.uuid[0:4], + uuid=john.uuid) + img_path2 = 'garden/{uuid_short}/{uuid}_garden.png'.format( + uuid_short=john.uuid[0:4], + uuid=john.uuid) + + assert not default_storage.exists(img_path1) + assert not default_storage.exists(img_path2) + + response = app.get('/accounts/edit/') + form = response.form + form.set('edit-profile-cityscape_image', Upload('tests/cityscape.png')) + response = form.submit() + + assert default_storage.exists(img_path1) + assert not default_storage.exists(img_path2) + + response = app.get('/accounts/edit/') + form = response.form + form.set('edit-profile-garden_image', Upload('tests/garden.png')) + response = form.submit() + + assert default_storage.exists(img_path1) + assert default_storage.exists(img_path2) + + response = app.get('/accounts/edit/') + form = response.form + form.set('edit-profile-cityscape_image-clear', True) + response = form.submit() + + assert not default_storage.exists(img_path1) + assert default_storage.exists(img_path2) + + response = app.get('/accounts/edit/') + form = response.form + form.set('edit-profile-garden_image-clear', True) + response = form.submit() + + assert not default_storage.exists(img_path1) + assert not default_storage.exists(img_path2) + + +def test_images_account_registration(db, app, admin, mailoutbox, monkeypatch, monkeyrequest): + from webtest import Upload + from authentic2.middleware import StoreRequestMiddleware + from django.test import RequestFactory + + Attribute.objects.create(name='cityscape_image', label='cityscape', kind='image', + asked_on_registration=True) + Attribute.objects.create(name='garden_image', label='garden', kind='image', + asked_on_registration=True) + qs = User.objects.filter(first_name='John') + + response = app.get('/accounts/register/') + form = response.form + form.set('email', 'john.doe@example.com') + response = form.submit().follow() + assert 'john.doe@example.com' in response + url = get_link_from_mail(mailoutbox[0]) + response = app.get(url) + + form = response.form + assert form.get('cityscape_image') + assert form.get('garden_image') + form.set('first_name', 'John') + form.set('last_name', 'Doe') + form.set('cityscape_image', Upload('tests/cityscape.png')) + form.set('garden_image', Upload('tests/garden.png')) + form.set('password1', '12345abcdA') + form.set('password2', '12345abcdA') + response = form.submit() + + john = qs.get() + monkeypatch.setattr(StoreRequestMiddleware, 'get_request', lambda cls: monkeyrequest) + assert john.attributes.cityscape_image + assert john.attributes.garden_image + monkeypatch.undo() + john.delete() diff --git a/tests/test_manager.py b/tests/test_manager.py index f9ef9471..0fc6a2b0 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -105,17 +105,18 @@ def test_manager_user_password_reset(app, superuser, simple_user): resp = login(app, superuser, reverse('a2-manager-user-detail', kwargs={'pk': simple_user.pk})) assert len(mail.outbox) == 0 - resp = resp.form.submit('password_reset') - assert 'A mail was sent to' in resp - assert len(mail.outbox) == 1 - url = get_link_from_mail(mail.outbox[0]) - relative_url = url.split('testserver')[1] - resp = app.get('/logout/').maybe_follow() - resp = app.get(relative_url, status=200) - resp.form.set('new_password1', '1234==aA') - resp.form.set('new_password2', '1234==aA') - resp = resp.form.submit().follow() - assert str(app.session['_auth_user_id']) == str(simple_user.pk) + if resp.form.enctype == u'application/x-www-form-urlencoded': + resp = resp.form.submit('password_reset') + assert 'A mail was sent to' in resp + assert len(mail.outbox) == 1 + url = get_link_from_mail(mail.outbox[0]) + relative_url = url.split('testserver')[1] + resp = app.get('/logout/').maybe_follow() + resp = app.get(relative_url, status=200) + resp.form.set('new_password1', '1234==aA') + resp.form.set('new_password2', '1234==aA') + resp = resp.form.submit().follow() + assert str(app.session['_auth_user_id']) == str(simple_user.pk) def test_manager_user_detail_by_uuid(app, superuser, simple_user): diff --git a/tests/test_profile.py b/tests/test_profile.py index 7c3497dd..9dbe8064 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -36,9 +36,10 @@ def test_account_edit_view(app, simple_user): assert attribute.get_value(simple_user) == '0123456789' resp = app.get(url, status=200) - resp.form.set('edit-profile-phone', '9876543210') - resp = resp.form.submit('cancel').follow() - assert attribute.get_value(simple_user) == '0123456789' + if resp.form.enctype == u'application/x-www-form-urlencoded': + resp.form.set('edit-profile-phone', '9876543210') + resp = resp.form.submit('cancel').follow() + assert attribute.get_value(simple_user) == '0123456789' attribute.set_value(simple_user, '0123456789', verified=True) resp = app.get(url, status=200) @@ -74,10 +75,11 @@ def test_account_edit_next_url(app, simple_user, external_redirect_next_url, ass assert attribute.get_value(simple_user) == '0123456789' resp = app.get(url + '?next=%s' % external_redirect_next_url, status=200) - resp.form.set('edit-profile-phone', '1234') - resp = resp.form.submit('cancel') - assert_external_redirect(resp, reverse('account_management')) - assert attribute.get_value(simple_user) == '0123456789' + if resp.form.enctype == u'application/x-www-form-urlencoded': + resp.form.set('edit-profile-phone', '1234') + resp = resp.form.submit('cancel') + assert_external_redirect(resp, reverse('account_management')) + assert attribute.get_value(simple_user) == '0123456789' def test_account_edit_scopes(app, simple_user): diff --git a/tox.ini b/tox.ini index 5fc36428..ec51509a 100644 --- a/tox.ini +++ b/tox.ini @@ -47,6 +47,8 @@ deps = httmock pytz pytest-freezegun + pillow + sorl-thumbnail commands = ./getlasso.sh authentic: py.test {env:FAST:} {env:REUSEDB:} {env:COVERAGE:} {posargs:tests/ --random} -- 2.19.0