0001-profile_views-address-autocomplete-field-41919.patch
src/authentic2/api_urls.py | ||
---|---|---|
28 | 28 |
name='a2-api-role-members'), |
29 | 29 |
url(r'^check-password/$', api_views.check_password, name='a2-api-check-password'), |
30 | 30 |
url(r'^validate-password/$', api_views.validate_password, name='a2-api-validate-password'), |
31 |
url(r'^address-autocomplete/$', api_views.address_autocomplete, name='a2-api-address-autocomplete'), |
|
31 | 32 |
] |
32 | 33 | |
33 | 34 |
urlpatterns += api_views.router.urls |
src/authentic2/api_views.py | ||
---|---|---|
21 | 21 |
from pytz.exceptions import AmbiguousTimeError |
22 | 22 |
import django |
23 | 23 |
from django.db import models |
24 |
from django.conf import settings |
|
24 | 25 |
from django.contrib.auth import get_user_model |
25 | 26 |
from django.contrib.auth.hashers import identify_hasher |
26 | 27 |
from django.core.exceptions import MultipleObjectsReturned |
... | ... | |
33 | 34 |
from django.shortcuts import get_object_or_404 |
34 | 35 | |
35 | 36 |
from django_rbac.utils import get_ou_model, get_role_model |
37 |
import requests |
|
38 |
from requests.exceptions import RequestException |
|
36 | 39 | |
37 | 40 |
from rest_framework import serializers, pagination |
38 | 41 |
from rest_framework.validators import UniqueTogetherValidator |
... | ... | |
1038 | 1041 |
return result, status.HTTP_200_OK |
1039 | 1042 | |
1040 | 1043 |
validate_password = ValidatePasswordAPI.as_view() |
1044 | ||
1045 | ||
1046 |
class AddressAutocompleteAPI(APIView): |
|
1047 |
permission_classes = (permissions.IsAuthenticated,) |
|
1048 | ||
1049 |
def get(self, request): |
|
1050 |
if not getattr(settings, 'ADDRESS_AUTOCOMPLETE_URL', None): |
|
1051 |
return Response({}) |
|
1052 |
try: |
|
1053 |
response = requests.get( |
|
1054 |
settings.ADDRESS_AUTOCOMPLETE_URL, |
|
1055 |
params=request.GET |
|
1056 |
) |
|
1057 |
response.raise_for_status() |
|
1058 |
return Response(response.json()) |
|
1059 |
except RequestException: |
|
1060 |
return Response({}) |
|
1061 | ||
1062 | ||
1063 |
address_autocomplete = AddressAutocompleteAPI.as_view() |
src/authentic2/attribute_kinds.py | ||
---|---|---|
23 | 23 |
from itertools import chain |
24 | 24 | |
25 | 25 |
from django import forms |
26 |
from django.conf import settings |
|
26 | 27 |
from django.core.exceptions import ValidationError |
27 | 28 |
from django.core.validators import RegexValidator |
29 |
from django.urls import reverse |
|
28 | 30 |
from django.utils import six, formats |
29 | 31 |
from django.utils.translation import ugettext_lazy as _, pgettext_lazy |
30 | 32 |
from django.utils import html |
... | ... | |
110 | 112 |
] |
111 | 113 | |
112 | 114 | |
115 |
class AddressAutocompleteInput(forms.Select): |
|
116 |
template_name = 'authentic2/widgets/address_autocomplete.html' |
|
117 | ||
118 |
class Media: |
|
119 |
js = [ |
|
120 |
settings.SELECT2_JS, |
|
121 |
'authentic2/js/address_autocomplete.js', |
|
122 |
] |
|
123 |
css = { |
|
124 |
'screen': [settings.SELECT2_CSS], |
|
125 |
} |
|
126 | ||
127 |
def __init__(self, **kwargs): |
|
128 |
super().__init__(**kwargs) |
|
129 |
self.attrs['data-select2-url'] = reverse('a2-api-address-autocomplete') |
|
130 |
self.attrs['class'] = 'address-autocomplete' |
|
131 | ||
132 | ||
133 |
class AddressAutocompleteField(forms.CharField): |
|
134 |
widget = AddressAutocompleteInput |
|
135 | ||
136 | ||
113 | 137 |
@to_iter |
114 | 138 |
def get_title_choices(): |
115 | 139 |
return app_settings.A2_ATTRIBUTE_KIND_TITLE_CHOICES or DEFAULT_TITLE_CHOICES |
... | ... | |
269 | 293 |
'rest_framework_field_class': BirthdateRestField, |
270 | 294 |
'free_text_search': date_free_text_search, |
271 | 295 |
}, |
296 |
{ |
|
297 |
'label': _('address (autocomplete)'), |
|
298 |
'name': 'address_auto', |
|
299 |
'field_class': AddressAutocompleteField, |
|
300 |
}, |
|
272 | 301 |
{ |
273 | 302 |
'label': _('french postcode'), |
274 | 303 |
'name': 'fr_postcode', |
src/authentic2/manager/static/authentic2/manager/css/style.css | ||
---|---|---|
248 | 248 |
margin-right: 1em; |
249 | 249 |
} |
250 | 250 | |
251 |
.ui-dialog-content span.select2-container { |
|
251 |
.ui-dialog-content span.select2-container, |
|
252 |
form .widget span.select2-container { |
|
252 | 253 |
width: 100% !important; |
253 | 254 |
} |
254 | 255 |
src/authentic2/manager/user_views.py | ||
---|---|---|
315 | 315 |
if not self.object.username and self.object.ou and not self.object.ou.show_username: |
316 | 316 |
fields.remove('username') |
317 | 317 |
for attribute in Attribute.objects.all(): |
318 |
if attribute.name == 'address_autocomplete': |
|
319 |
continue |
|
318 | 320 |
fields.append(attribute.name) |
319 | 321 |
if self.request.user.is_superuser and \ |
320 | 322 |
'is_superuser' not in self.fields: |
src/authentic2/static/authentic2/js/address_autocomplete.js | ||
---|---|---|
1 |
$(function() { |
|
2 |
$('select.address-autocomplete').select2({ |
|
3 |
ajax: { |
|
4 |
delay: 250, |
|
5 |
dataType: 'json', |
|
6 |
data: function(params) { |
|
7 |
return {q: params.term, page_limit: 10}; |
|
8 |
}, |
|
9 |
processResults: function (data, params) { |
|
10 |
return {results: data.data}; |
|
11 |
}, |
|
12 |
url: function (params) { |
|
13 |
return $(this).data('select2-url') |
|
14 |
} |
|
15 |
} |
|
16 |
}).on('select2:select', function(e) { |
|
17 |
var data = e.params.data; |
|
18 |
if (data) { |
|
19 |
var address = undefined; |
|
20 |
if (typeof data.address == "object") { |
|
21 |
address = data.address; |
|
22 |
} else { |
|
23 |
address = data; |
|
24 |
} |
|
25 |
var road = address.road || address.nom_rue; |
|
26 |
var house_number = address.house_number || address.numero; |
|
27 |
var city = address.city || address.nom_commune; |
|
28 |
var postcode = address.postcode || address.code_postal; |
|
29 |
var number_and_street = null; |
|
30 |
if (house_number && road) { |
|
31 |
number_and_street = house_number + ' ' + road; |
|
32 |
} else { |
|
33 |
number_and_street = road; |
|
34 |
} |
|
35 |
$('#id_address').val(number_and_street); |
|
36 |
$('#id_city').val(city); |
|
37 |
$('#id_zipcode').val(postcode); |
|
38 |
} |
|
39 |
}); |
|
40 |
$('#id_address, #id_city, #id_zipcode').attr('readonly', 'readonly'); |
|
41 |
$('#manual-address').on('change', function() { |
|
42 |
$('#id_address, #id_city, #id_zipcode').attr('readonly', this.checked ? null : 'readonly'); |
|
43 |
}); |
|
44 |
if ($('#id_address').val() || $('#id_city').val() || $('#id_zipcode').val()) { |
|
45 |
var data = { |
|
46 |
id: 1, |
|
47 |
text: '' |
|
48 |
} |
|
49 |
$.each(['#id_address', '#id_zipcode', '#id_city'], function(idx, value) { |
|
50 |
if ($(value).val()) { |
|
51 |
if (data.text) { |
|
52 |
data.text += ' '; |
|
53 |
} |
|
54 |
data.text += $(value).val(); |
|
55 |
} |
|
56 |
}) |
|
57 |
var newOption = new Option(data.text, data.id, false, false); |
|
58 |
$('select.address-autocomplete').append(newOption).trigger('change'); |
|
59 |
} |
|
60 |
}); |
src/authentic2/templates/authentic2/widgets/address_autocomplete.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
{% include "django/forms/widgets/select.html" %} |
|
3 |
<div> |
|
4 |
<label><input id="manual-address" type="checkbox">{% trans "Manually enter the address" %}</label> |
|
5 |
</div> |
src/authentic2/views.py | ||
---|---|---|
133 | 133 | |
134 | 134 |
def get_form_kwargs(self, **kwargs): |
135 | 135 |
kwargs = super(EditProfile, self).get_form_kwargs(**kwargs) |
136 |
kwargs['prefix'] = 'edit-profile' |
|
137 | 136 |
kwargs['next_url'] = utils.select_next_url(self.request, reverse('account_management')) |
138 | 137 |
return kwargs |
139 | 138 | |
... | ... | |
141 | 140 |
return utils.select_next_url( |
142 | 141 |
self.request, |
143 | 142 |
default=reverse('account_management'), |
144 |
field_name='edit-profile-next_url',
|
|
143 |
field_name='next_url', |
|
145 | 144 |
include_post=True) |
146 | 145 | |
147 | 146 |
def post(self, request, *args, **kwargs): |
tests/test_api.py | ||
---|---|---|
18 | 18 | |
19 | 19 |
import datetime |
20 | 20 |
import json |
21 |
import mock |
|
21 | 22 |
import pytest |
22 | 23 |
import random |
23 | 24 |
import uuid |
... | ... | |
36 | 37 | |
37 | 38 |
from django_rbac.models import SEARCH_OP |
38 | 39 |
from django_rbac.utils import get_role_model, get_ou_model |
40 |
from requests.models import Response |
|
39 | 41 | |
40 | 42 |
from authentic2.a2_rbac.models import Role |
41 | 43 |
from authentic2.a2_rbac.utils import get_default_ou |
... | ... | |
1926 | 1928 |
resp = app.get('/api/users/find_duplicates/', params=params) |
1927 | 1929 |
assert len(resp.json['data']) == 2 |
1928 | 1930 |
assert resp.json['data'][0]['id'] == homonym.pk |
1931 | ||
1932 | ||
1933 |
class MockedRequestResponse(mock.Mock): |
|
1934 |
status_code = 200 |
|
1935 | ||
1936 |
def json(self): |
|
1937 |
return json.loads(self.content) |
|
1938 | ||
1939 | ||
1940 |
def test_api_address_autocomplete(app, admin, settings): |
|
1941 |
app.authorization = ('Basic', (admin.username, admin.username)) |
|
1942 | ||
1943 |
settings.ADDRESS_AUTOCOMPLETE_URL = 'example.com' |
|
1944 | ||
1945 |
params = {'q': '42 avenue'} |
|
1946 |
with mock.patch('authentic2.api_views.requests.get') as requests_get: |
|
1947 |
mock_resp = Response() |
|
1948 |
mock_resp.status_code = 500 |
|
1949 |
requests_get.return_value = mock_resp |
|
1950 |
resp = app.get('/api/address-autocomplete/', params=params) |
|
1951 |
assert resp.json == {} |
|
1952 |
assert requests_get.call_args_list[0][0][0] == 'example.com' |
|
1953 |
assert requests_get.call_args_list[0][1]['params'] == {'q': ['42 avenue']} |
|
1954 |
with mock.patch('authentic2.api_views.requests.get') as requests_get: |
|
1955 |
mock_resp = Response() |
|
1956 |
mock_resp.status_code = 404 |
|
1957 |
requests_get.return_value = mock_resp |
|
1958 |
resp = app.get('/api/address-autocomplete/', params=params) |
|
1959 |
assert resp.json == {} |
|
1960 |
with mock.patch('authentic2.api_views.requests.get') as requests_get: |
|
1961 |
requests_get.return_value = MockedRequestResponse(content=json.dumps({'data': {'foo': 'bar'}})) |
|
1962 |
resp = app.get('/api/address-autocomplete/', params=params) |
|
1963 |
assert resp.json == {'data': {'foo': 'bar'}} |
|
1964 | ||
1965 |
settings.ADDRESS_AUTOCOMPLETE_URL = None |
|
1966 |
with mock.patch('authentic2.api_views.requests.get') as requests_get: |
|
1967 |
resp = app.get('/api/address-autocomplete/', params=params) |
|
1968 |
assert resp.json == {} |
|
1969 |
assert requests_get.call_args_list == [] |
|
1970 | ||
1971 |
del settings.ADDRESS_AUTOCOMPLETE_URL |
|
1972 |
with mock.patch('authentic2.api_views.requests.get') as requests_get: |
|
1973 |
resp = app.get('/api/address-autocomplete/', params=params) |
|
1974 |
assert resp.json == {} |
|
1975 |
assert requests_get.call_args_list == [] |
tests/test_attribute_kinds.py | ||
---|---|---|
456 | 456 |
# verify we can clear the image |
457 | 457 |
response = app.get('/accounts/edit/') |
458 | 458 |
form = response.form |
459 |
form.set('edit-profile-first_name', 'John')
|
|
460 |
form.set('edit-profile-last_name', 'Doe')
|
|
461 |
form.set('edit-profile-cityscape_image-clear', True)
|
|
459 |
form.set('first_name', 'John') |
|
460 |
form.set('last_name', 'Doe') |
|
461 |
form.set('cityscape_image-clear', True) |
|
462 | 462 |
response = form.submit() |
463 | 463 |
assert john().attributes.cityscape_image == None |
464 | 464 | |
... | ... | |
470 | 470 |
# verify 201x201 image is accepted and resized |
471 | 471 |
response = app.get('/accounts/edit/') |
472 | 472 |
form = response.form |
473 |
form.set('edit-profile-cityscape_image', Upload('tests/201x201.jpg'))
|
|
473 |
form.set('cityscape_image', Upload('tests/201x201.jpg')) |
|
474 | 474 |
response = form.submit() |
475 | 475 |
with PIL.Image.open(os.path.join(settings.MEDIA_ROOT, john().attributes.cityscape_image.name)) as image: |
476 | 476 |
assert image.width == 200 |
... | ... | |
480 | 480 |
# verify file input mentions image files |
481 | 481 |
response = app.get('/accounts/edit/') |
482 | 482 |
form = response.form |
483 |
assert form['edit-profile-cityscape_image'].attrs['accept'] == 'image/*'
|
|
483 |
assert form['cityscape_image'].attrs['accept'] == 'image/*' |
|
484 | 484 | |
485 | 485 | |
486 | 486 |
def test_multiple_attribute_setter(db, app, simple_user): |
tests/test_profile.py | ||
---|---|---|
47 | 47 |
kind='boolean', user_visible=True, user_editable=True) |
48 | 48 | |
49 | 49 |
resp = old_resp = app.get(url, status=200) |
50 |
resp.form['edit-profile-phone'] = '1234'
|
|
51 |
assert resp.form['edit-profile-phone'].attrs['type'] == 'tel'
|
|
52 |
resp.form['edit-profile-title'] = 'Mrs'
|
|
53 |
resp.form['edit-profile-agreement'] = False
|
|
50 |
resp.form['phone'] = '1234' |
|
51 |
assert resp.form['phone'].attrs['type'] == 'tel' |
|
52 |
resp.form['title'] = 'Mrs' |
|
53 |
resp.form['agreement'] = False |
|
54 | 54 |
resp = resp.form.submit() |
55 | 55 |
# verify that missing next_url in POST is ok |
56 | 56 |
assert resp['Location'].endswith(reverse('account_management')) |
... | ... | |
70 | 70 |
] |
71 | 71 | |
72 | 72 |
resp = app.get(url, status=200) |
73 |
resp.form.set('edit-profile-phone', '0123456789')
|
|
73 |
resp.form.set('phone', '0123456789') |
|
74 | 74 |
resp = resp.form.submit().follow() |
75 | 75 |
assert phone.get_value(simple_user) == '0123456789' |
76 | 76 | |
77 | 77 |
resp = app.get(url, status=200) |
78 |
resp.form.set('edit-profile-phone', '9876543210')
|
|
78 |
resp.form.set('phone', '9876543210') |
|
79 | 79 |
resp = resp.form.submit('cancel').follow() |
80 | 80 |
assert phone.get_value(simple_user) == '0123456789' |
81 | 81 | |
... | ... | |
83 | 83 |
title.set_value(simple_user, 'Mr', verified=True) |
84 | 84 |
agreement.set_value(simple_user, True, verified=True) |
85 | 85 |
resp = app.get(url, status=200) |
86 |
assert 'edit-profile-phone' not in resp.form.fields
|
|
87 |
assert 'edit-profile-title' not in resp.form.fields
|
|
88 |
assert 'edit-profile-agreement' not in resp.form.fields
|
|
89 |
assert 'readonly' in resp.form['edit-profile-phone@disabled'].attrs
|
|
90 |
assert resp.form['edit-profile-phone@disabled'].value == '0123456789'
|
|
91 |
assert resp.form['edit-profile-title@disabled'].value == 'Mr'
|
|
92 |
assert resp.form['edit-profile-agreement@disabled'].value == 'Yes'
|
|
93 |
resp.form.set('edit-profile-phone@disabled', '1234')
|
|
94 |
resp.form.set('edit-profile-title@disabled', 'Mrs')
|
|
95 |
resp.form.set('edit-profile-agreement@disabled', 'False')
|
|
86 |
assert 'phone' not in resp.form.fields |
|
87 |
assert 'title' not in resp.form.fields |
|
88 |
assert 'agreement' not in resp.form.fields |
|
89 |
assert 'readonly' in resp.form['phone@disabled'].attrs |
|
90 |
assert resp.form['phone@disabled'].value == '0123456789' |
|
91 |
assert resp.form['title@disabled'].value == 'Mr' |
|
92 |
assert resp.form['agreement@disabled'].value == 'Yes' |
|
93 |
resp.form.set('phone@disabled', '1234') |
|
94 |
resp.form.set('title@disabled', 'Mrs') |
|
95 |
resp.form.set('agreement@disabled', 'False') |
|
96 | 96 |
resp = resp.form.submit().follow() |
97 | 97 |
assert phone.get_value(simple_user) == '0123456789' |
98 | 98 |
assert title.get_value(simple_user) == 'Mr' |
... | ... | |
106 | 106 |
phone.disabled = True |
107 | 107 |
phone.save() |
108 | 108 |
resp = app.get(url, status=200) |
109 |
assert 'edit-profile-phone@disabled' not in resp
|
|
110 |
assert 'edit-profile-title@disabled' in resp
|
|
111 |
assert 'edit-profile-agreement@disabled' in resp
|
|
109 |
assert 'phone@disabled' not in resp |
|
110 |
assert 'title@disabled' in resp |
|
111 |
assert 'agreement@disabled' in resp |
|
112 | 112 |
assert phone.get_value(simple_user) == '0123456789' |
113 | 113 | |
114 | 114 | |
... | ... | |
122 | 122 |
user_editable=True) |
123 | 123 | |
124 | 124 |
resp = app.get(url + '?next=%s' % external_redirect_next_url, status=200) |
125 |
resp.form.set('edit-profile-phone', '0123456789')
|
|
125 |
resp.form.set('phone', '0123456789') |
|
126 | 126 |
resp = resp.form.submit() |
127 | 127 |
assert_external_redirect(resp, reverse('account_management')) |
128 | 128 |
assert attribute.get_value(simple_user) == '0123456789' |
129 | 129 | |
130 | 130 |
resp = app.get(url + '?next=%s' % external_redirect_next_url, status=200) |
131 |
resp.form.set('edit-profile-phone', '1234')
|
|
131 |
resp.form.set('phone', '1234') |
|
132 | 132 |
resp = resp.form.submit('cancel') |
133 | 133 |
assert_external_redirect(resp, reverse('account_management')) |
134 | 134 |
assert attribute.get_value(simple_user) == '0123456789' |
... | ... | |
153 | 153 |
scopes='address') |
154 | 154 | |
155 | 155 |
def get_fields(resp): |
156 |
return set(key.split('edit-profile-')[1]
|
|
157 |
for key in resp.form.fields.keys() if key and key.startswith('edit-profile-')) |
|
156 |
return set(key for key in resp.form.fields.keys() if key and key not in ['csrfmiddlewaretoken', 'cancel'])
|
|
157 | ||
158 | 158 |
resp = app.get(url, status=200) |
159 | 159 |
assert get_fields(resp) == set(['first_name', 'last_name', 'phone', 'mobile', 'city', 'zipcode', 'next_url']) |
160 | 160 | |
... | ... | |
185 | 185 |
utils.login(app, simple_user) |
186 | 186 |
url = reverse('profile_edit') |
187 | 187 |
response = app.get(url, status=200) |
188 |
assert len(response.pyquery('input[type="radio"][name="edit-profile-title"]')) == 2
|
|
189 |
assert len(response.pyquery('input[type="radio"][name="edit-profile-title"][readonly="true"]')) == 0
|
|
190 |
assert len(response.pyquery('select[name="edit-profile-title"]')) == 0
|
|
188 |
assert len(response.pyquery('input[type="radio"][name="title"]')) == 2 |
|
189 |
assert len(response.pyquery('input[type="radio"][name="title"][readonly="true"]')) == 0 |
|
190 |
assert len(response.pyquery('select[name="title"]')) == 0 |
|
191 | 191 | |
192 | 192 |
simple_user.verified_attributes.title = 'Monsieur' |
193 | 193 | |
194 | 194 |
response = app.get(url, status=200) |
195 |
assert len(response.pyquery('input[type="radio"][name="edit-profile-title"]')) == 0
|
|
196 |
assert len(response.pyquery('input[type="text"][name="edit-profile-title@disabled"][readonly]')) == 1
|
|
195 |
assert len(response.pyquery('input[type="radio"][name="title"]')) == 0 |
|
196 |
assert len(response.pyquery('input[type="text"][name="title@disabled"][readonly]')) == 1 |
|
197 | 197 | |
198 | 198 | |
199 | 199 |
def test_account_view(app, simple_user, settings): |
tests/test_registration.py | ||
---|---|---|
379 | 379 |
assert u'Prénom' not in response.text |
380 | 380 | |
381 | 381 |
response = app.get(reverse('profile_edit')) |
382 |
assert 'edit-profile-profession' in response.form.fields
|
|
383 |
assert 'edit-profile-prenom' not in response.form.fields
|
|
384 |
assert 'edit-profile-nom' not in response.form.fields
|
|
382 |
assert 'profession' in response.form.fields |
|
383 |
assert 'prenom' not in response.form.fields |
|
384 |
assert 'nom' not in response.form.fields |
|
385 | 385 | |
386 |
assert response.pyquery('[for=id_edit-profile-profession]')
|
|
387 |
assert not response.pyquery('[for=id_edit-profile-profession].form-field-required')
|
|
388 |
response.form.set('edit-profile-profession', 'pompier')
|
|
386 |
assert response.pyquery('[for=id_profession]') |
|
387 |
assert not response.pyquery('[for=id_profession].form-field-required') |
|
388 |
response.form.set('profession', 'pompier') |
|
389 | 389 |
response = response.form.submit() |
390 | 390 |
assert urlparse(response['Location']).path == reverse('account_management') |
391 | 391 |
tests/test_user_manager.py | ||
---|---|---|
733 | 733 |
assert not user.email_verified |
734 | 734 | |
735 | 735 | |
736 |
def test_manager_edit_user_address_autocomplete(app, simple_user, superuser_or_admin): |
|
737 |
url = u'/manage/users/%s/edit/' % simple_user.pk |
|
738 |
login(app, superuser_or_admin, '/manage/') |
|
739 | ||
740 |
Attribute.objects.create( |
|
741 |
name='address_autocomplete', label='Address (autocomplete)', |
|
742 |
kind='address_auto', user_visible=True, user_editable=True) |
|
743 | ||
744 |
resp = app.get(url) |
|
745 |
assert resp.html.find('select', {'name': 'address_autocomplete'}) |
|
746 |
assert resp.html.find('input', {'id': 'manual-address'}) |
|
747 | ||
748 | ||
736 | 749 |
def test_manager_email_verified_column_user(app, simple_user, superuser_or_admin): |
737 | 750 |
login(app, superuser_or_admin, '/manage/') |
738 | 751 | |
... | ... | |
793 | 806 |
assert resp.html.find('input', {'name': 'username'}) |
794 | 807 | |
795 | 808 | |
809 |
def test_manager_user_address_autocomplete_field(app, superuser, simple_user): |
|
810 |
login(app, superuser, '/manage/') |
|
811 |
Attribute.objects.create( |
|
812 |
name='address_autocomplete', label='Address (autocomplete)', |
|
813 |
kind='address_auto', user_visible=True, user_editable=True) |
|
814 |
resp = app.get(reverse('a2-manager-user-detail', kwargs={'pk': simple_user.id})) |
|
815 |
assert not resp.html.find('select', {'name': 'address_autocomplete'}) |
|
816 |
assert not resp.html.find('input', {'id': 'manual-address'}) |
|
817 | ||
818 | ||
796 | 819 |
def test_manager_user_roles_visibility(app, simple_user, admin, ou1, ou2): |
797 | 820 |
Role = get_role_model() |
798 | 821 |
role1 = Role.objects.create(name='Role 1', slug='role1', ou=ou1) |
799 |
- |