Refs #31026 -- Moved Template tests to separate class.

This commit is contained in:
David Smith 2021-09-10 17:50:05 +01:00 committed by Mariusz Felisiak
parent 4555aa0a48
commit 91e8b95d5b
1 changed files with 343 additions and 234 deletions

View File

@ -24,6 +24,11 @@ from django.utils.datastructures import MultiValueDict
from django.utils.safestring import mark_safe
class FrameworkForm(Form):
name = CharField()
language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect)
class Person(Form):
first_name = CharField()
last_name = CharField()
@ -36,6 +41,14 @@ class PersonNew(Form):
birthday = DateField()
class SongForm(Form):
name = CharField()
composers = MultipleChoiceField(
choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')],
widget=CheckboxSelectMultiple,
)
class MultiValueDictLike(dict):
def getlist(self, key):
return [self[key]]
@ -580,10 +593,6 @@ class FormsTestCase(SimpleTestCase):
def test_forms_with_radio(self):
# Add widget=RadioSelect to use that widget with a ChoiceField.
class FrameworkForm(Form):
name = CharField()
language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect)
f = FrameworkForm(auto_id=False)
self.assertHTMLEqual(str(f['language']), """<div>
<div><label><input type="radio" name="language" value="P" required> Python</label></div>
@ -649,16 +658,6 @@ Java</label></div>
</div></p>"""
)
# Test iterating on individual radios in a template
t = Template('{% for radio in form.language %}<div class="myradio">{{ radio }}</div>{% endfor %}')
self.assertHTMLEqual(
t.render(Context({'form': f})),
"""<div class="myradio"><label for="id_language_0">
<input id="id_language_0" name="language" type="radio" value="P" required> Python</label></div>
<div class="myradio"><label for="id_language_1">
<input id="id_language_1" name="language" type="radio" value="J" required> Java</label></div>"""
)
def test_form_with_iterable_boundfield(self):
class BeatleForm(Form):
name = ChoiceField(
@ -862,13 +861,6 @@ Java</label></div>
def test_multiple_choice_checkbox(self):
# MultipleChoiceField can also be used with the CheckboxSelectMultiple widget.
class SongForm(Form):
name = CharField()
composers = MultipleChoiceField(
choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')],
widget=CheckboxSelectMultiple,
)
f = SongForm(auto_id=False)
self.assertHTMLEqual(str(f['composers']), """<div>
<div><label><input type="checkbox" name="composers" value="J"> John Lennon</label></div>
@ -884,12 +876,6 @@ Java</label></div>
<div><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></div>
<div><label><input checked type="checkbox" name="composers" value="P"> Paul McCartney</label></div>
</div>""")
# Test iterating on individual checkboxes in a template
t = Template('{% for checkbox in form.composers %}<div class="mycheckbox">{{ checkbox }}</div>{% endfor %}')
self.assertHTMLEqual(t.render(Context({'form': f})), """<div class="mycheckbox"><label>
<input checked name="composers" type="checkbox" value="J"> John Lennon</label></div>
<div class="mycheckbox"><label>
<input checked name="composers" type="checkbox" value="P"> Paul McCartney</label></div>""")
def test_checkbox_auto_id(self):
# Regarding auto_id, CheckboxSelectMultiple is a special case. Each checkbox
@ -2582,213 +2568,6 @@ Password: <input type="password" name="password" required>
'<tr><th>File1:</th><td><input type="file" name="file1"></td></tr>',
)
def test_basic_processing_in_view(self):
class UserRegistration(Form):
username = CharField(max_length=10)
password1 = CharField(widget=PasswordInput)
password2 = CharField(widget=PasswordInput)
def clean(self):
if (self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and
self.cleaned_data['password1'] != self.cleaned_data['password2']):
raise ValidationError('Please make sure your passwords match.')
return self.cleaned_data
def my_function(method, post_data):
if method == 'POST':
form = UserRegistration(post_data, auto_id=False)
else:
form = UserRegistration(auto_id=False)
if form.is_valid():
return 'VALID: %r' % sorted(form.cleaned_data.items())
t = Template(
'<form method="post">\n'
'<table>\n{{ form }}\n</table>\n<input type="submit" required>\n</form>'
)
return t.render(Context({'form': form}))
# Case 1: GET (an empty form, with no errors).)
self.assertHTMLEqual(my_function('GET', {}), """<form method="post">
<table>
<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" required></td></tr>
<tr><th>Password1:</th><td><input type="password" name="password1" required></td></tr>
<tr><th>Password2:</th><td><input type="password" name="password2" required></td></tr>
</table>
<input type="submit" required>
</form>""")
# Case 2: POST with erroneous data (a redisplayed form, with errors).)
self.assertHTMLEqual(
my_function('POST', {'username': 'this-is-a-long-username', 'password1': 'foo', 'password2': 'bar'}),
"""<form method="post">
<table>
<tr><td colspan="2"><ul class="errorlist nonfield"><li>Please make sure your passwords match.</li></ul></td></tr>
<tr><th>Username:</th><td><ul class="errorlist">
<li>Ensure this value has at most 10 characters (it has 23).</li></ul>
<input type="text" name="username" value="this-is-a-long-username" maxlength="10" required></td></tr>
<tr><th>Password1:</th><td><input type="password" name="password1" required></td></tr>
<tr><th>Password2:</th><td><input type="password" name="password2" required></td></tr>
</table>
<input type="submit" required>
</form>"""
)
# Case 3: POST with valid data (the success message).)
self.assertEqual(
my_function('POST', {'username': 'adrian', 'password1': 'secret', 'password2': 'secret'}),
"VALID: [('password1', 'secret'), ('password2', 'secret'), ('username', 'adrian')]"
)
def test_templates_with_forms(self):
class UserRegistration(Form):
username = CharField(max_length=10, help_text="Good luck picking a username that doesn't already exist.")
password1 = CharField(widget=PasswordInput)
password2 = CharField(widget=PasswordInput)
def clean(self):
if (self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and
self.cleaned_data['password1'] != self.cleaned_data['password2']):
raise ValidationError('Please make sure your passwords match.')
return self.cleaned_data
# You have full flexibility in displaying form fields in a template. Just pass a
# Form instance to the template, and use "dot" access to refer to individual
# fields. Note, however, that this flexibility comes with the responsibility of
# displaying all the errors, including any that might not be associated with a
# particular field.
t = Template('''<form>
{{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p>
{{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p>
{{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p>
<input type="submit" required>
</form>''')
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form>
<p><label>Your username: <input type="text" name="username" maxlength="10" required></label></p>
<p><label>Password: <input type="password" name="password1" required></label></p>
<p><label>Password (again): <input type="password" name="password2" required></label></p>
<input type="submit" required>
</form>""")
self.assertHTMLEqual(
t.render(Context({'form': UserRegistration({'username': 'django'}, auto_id=False)})),
"""<form>
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" required></label></p>
<ul class="errorlist"><li>This field is required.</li></ul><p>
<label>Password: <input type="password" name="password1" required></label></p>
<ul class="errorlist"><li>This field is required.</li></ul>
<p><label>Password (again): <input type="password" name="password2" required></label></p>
<input type="submit" required>
</form>"""
)
# Use form.[field].label to output a field's label. You can specify the label for
# a field by using the 'label' argument to a Field class. If you don't specify
# 'label', Django will use the field name with underscores converted to spaces,
# and the initial letter capitalized.
t = Template('''<form>
<p><label>{{ form.username.label }}: {{ form.username }}</label></p>
<p><label>{{ form.password1.label }}: {{ form.password1 }}</label></p>
<p><label>{{ form.password2.label }}: {{ form.password2 }}</label></p>
<input type="submit" required>
</form>''')
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form>
<p><label>Username: <input type="text" name="username" maxlength="10" required></label></p>
<p><label>Password1: <input type="password" name="password1" required></label></p>
<p><label>Password2: <input type="password" name="password2" required></label></p>
<input type="submit" required>
</form>""")
# User form.[field].label_tag to output a field's label with a <label> tag
# wrapped around it, but *only* if the given field has an "id" attribute.
# Recall from above that passing the "auto_id" argument to a Form gives each
# field an "id" attribute.
t = Template('''<form>
<p>{{ form.username.label_tag }} {{ form.username }}</p>
<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>
<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>
<input type="submit" required>
</form>''')
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form>
<p>Username: <input type="text" name="username" maxlength="10" required></p>
<p>Password1: <input type="password" name="password1" required></p>
<p>Password2: <input type="password" name="password2" required></p>
<input type="submit" required>
</form>""")
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id='id_%s')})), """<form>
<p><label for="id_username">Username:</label>
<input id="id_username" type="text" name="username" maxlength="10" required></p>
<p><label for="id_password1">Password1:</label>
<input type="password" name="password1" id="id_password1" required></p>
<p><label for="id_password2">Password2:</label>
<input type="password" name="password2" id="id_password2" required></p>
<input type="submit" required>
</form>""")
# User form.[field].help_text to output a field's help text. If the given field
# does not have help text, nothing will be output.
t = Template('''<form>
<p>{{ form.username.label_tag }} {{ form.username }}<br>{{ form.username.help_text }}</p>
<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>
<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>
<input type="submit" required>
</form>''')
self.assertHTMLEqual(
t.render(Context({'form': UserRegistration(auto_id=False)})),
"""<form>
<p>Username: <input type="text" name="username" maxlength="10" required><br>
Good luck picking a username that doesn&#x27;t already exist.</p>
<p>Password1: <input type="password" name="password1" required></p>
<p>Password2: <input type="password" name="password2" required></p>
<input type="submit" required>
</form>"""
)
self.assertEqual(
Template('{{ form.password1.help_text }}').render(Context({'form': UserRegistration(auto_id=False)})),
''
)
# To display the errors that aren't associated with a particular field -- e.g.,
# the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the
# template. If used on its own, it is displayed as a <ul> (or an empty string, if
# the list of errors is empty). You can also use it in {% if %} statements.
t = Template('''<form>
{{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p>
{{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p>
{{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p>
<input type="submit" required>
</form>''')
self.assertHTMLEqual(
t.render(Context({
'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
})),
"""<form>
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" required></label></p>
<p><label>Password: <input type="password" name="password1" required></label></p>
<p><label>Password (again): <input type="password" name="password2" required></label></p>
<input type="submit" required>
</form>"""
)
t = Template('''<form>
{{ form.non_field_errors }}
{{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p>
{{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p>
{{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p>
<input type="submit" required>
</form>''')
self.assertHTMLEqual(
t.render(Context({
'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
})),
"""<form>
<ul class="errorlist nonfield"><li>Please make sure your passwords match.</li></ul>
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" required></label></p>
<p><label>Password: <input type="password" name="password1" required></label></p>
<p><label>Password (again): <input type="password" name="password2" required></label></p>
<input type="submit" required>
</form>"""
)
def test_empty_permitted(self):
# Sometimes (pretty much in formsets) we want to allow a form to pass validation
# if it is completely empty. We can accomplish this by using the empty_permitted
@ -3900,3 +3679,333 @@ class RendererTests(SimpleTestCase):
custom = CustomRenderer()
form = CustomForm(renderer=custom)
self.assertEqual(form.renderer, custom)
class TemplateTests(SimpleTestCase):
def test_iterate_radios(self):
f = FrameworkForm(auto_id='id_%s')
t = Template(
'{% for radio in form.language %}'
'<div class="myradio">{{ radio }}</div>'
'{% endfor %}'
)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<div class="myradio"><label for="id_language_0">'
'<input id="id_language_0" name="language" type="radio" value="P" '
'required> Python</label></div>'
'<div class="myradio"><label for="id_language_1">'
'<input id="id_language_1" name="language" type="radio" value="J" '
'required> Java</label></div>',
)
def test_iterate_checkboxes(self):
f = SongForm({'composers': ['J', 'P']}, auto_id=False)
t = Template(
'{% for checkbox in form.composers %}'
'<div class="mycheckbox">{{ checkbox }}</div>'
'{% endfor %}'
)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<div class="mycheckbox"><label>'
'<input checked name="composers" type="checkbox" value="J"> '
'John Lennon</label></div>'
'<div class="mycheckbox"><label>'
'<input checked name="composers" type="checkbox" value="P"> '
'Paul McCartney</label></div>',
)
def test_templates_with_forms(self):
class UserRegistration(Form):
username = CharField(max_length=10, help_text=(
"Good luck picking a username that doesn't already exist."
))
password1 = CharField(widget=PasswordInput)
password2 = CharField(widget=PasswordInput)
def clean(self):
if (
self.cleaned_data.get('password1') and
self.cleaned_data.get('password2') and
self.cleaned_data['password1'] != self.cleaned_data['password2']
):
raise ValidationError('Please make sure your passwords match.')
return self.cleaned_data
# There is full flexibility in displaying form fields in a template.
# Just pass a Form instance to the template, and use "dot" access to
# refer to individual fields. However, this flexibility comes with the
# responsibility of displaying all the errors, including any that might
# not be associated with a particular field.
t = Template(
'<form>'
'{{ form.username.errors.as_ul }}'
'<p><label>Your username: {{ form.username }}</label></p>'
'{{ form.password1.errors.as_ul }}'
'<p><label>Password: {{ form.password1 }}</label></p>'
'{{ form.password2.errors.as_ul }}'
'<p><label>Password (again): {{ form.password2 }}</label></p>'
'<input type="submit" required>'
'</form>'
)
f = UserRegistration(auto_id=False)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<p><label>Your username: '
'<input type="text" name="username" maxlength="10" required></label></p>'
'<p><label>Password: '
'<input type="password" name="password1" required></label></p>'
'<p><label>Password (again): '
'<input type="password" name="password2" required></label></p>'
'<input type="submit" required>'
'</form>',
)
f = UserRegistration({'username': 'django'}, auto_id=False)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<p><label>Your username: '
'<input type="text" name="username" value="django" maxlength="10" required>'
'</label></p>'
'<ul class="errorlist"><li>This field is required.</li></ul><p>'
'<label>Password: '
'<input type="password" name="password1" required></label></p>'
'<ul class="errorlist"><li>This field is required.</li></ul>'
'<p><label>Password (again): '
'<input type="password" name="password2" required></label></p>'
'<input type="submit" required>'
'</form>',
)
# Use form.[field].label to output a field's label. 'label' for a field
# can by specified by using the 'label' argument to a Field class. If
# 'label' is not specified, Django will use the field name with
# underscores converted to spaces, and the initial letter capitalized.
t = Template(
'<form>'
'<p><label>{{ form.username.label }}: {{ form.username }}</label></p>'
'<p><label>{{ form.password1.label }}: {{ form.password1 }}</label></p>'
'<p><label>{{ form.password2.label }}: {{ form.password2 }}</label></p>'
'<input type="submit" required>'
'</form>'
)
f = UserRegistration(auto_id=False)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<p><label>Username: '
'<input type="text" name="username" maxlength="10" required></label></p>'
'<p><label>Password1: '
'<input type="password" name="password1" required></label></p>'
'<p><label>Password2: '
'<input type="password" name="password2" required></label></p>'
'<input type="submit" required>'
'</form>',
)
# Use form.[field].label_tag to output a field's label with a <label>
# tag wrapped around it, but *only* if the given field has an "id"
# attribute. Recall from above that passing the "auto_id" argument to a
# Form gives each field an "id" attribute.
t = Template(
'<form>'
'<p>{{ form.username.label_tag }} {{ form.username }}</p>'
'<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>'
'<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>'
'<input type="submit" required>'
'</form>'
)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<p>Username: '
'<input type="text" name="username" maxlength="10" required></p>'
'<p>Password1: <input type="password" name="password1" required></p>'
'<p>Password2: <input type="password" name="password2" required></p>'
'<input type="submit" required>'
'</form>',
)
f = UserRegistration(auto_id='id_%s')
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<p><label for="id_username">Username:</label>'
'<input id="id_username" type="text" name="username" maxlength="10" '
'required></p>'
'<p><label for="id_password1">Password1:</label>'
'<input type="password" name="password1" id="id_password1" required></p>'
'<p><label for="id_password2">Password2:</label>'
'<input type="password" name="password2" id="id_password2" required></p>'
'<input type="submit" required>'
'</form>',
)
# Use form.[field].help_text to output a field's help text. If the
# given field does not have help text, nothing will be output.
t = Template(
'<form>'
'<p>{{ form.username.label_tag }} {{ form.username }}<br>'
'{{ form.username.help_text }}</p>'
'<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>'
'<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>'
'<input type="submit" required>'
'</form>'
)
f = UserRegistration(auto_id=False)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<p>Username: '
'<input type="text" name="username" maxlength="10" required><br>'
'Good luck picking a username that doesn&#x27;t already exist.</p>'
'<p>Password1: <input type="password" name="password1" required></p>'
'<p>Password2: <input type="password" name="password2" required></p>'
'<input type="submit" required>'
'</form>',
)
self.assertEqual(
Template('{{ form.password1.help_text }}').render(Context({'form': f})),
'',
)
# To display the errors that aren't associated with a particular field
# e.g. the errors caused by Form.clean() -- use
# {{ form.non_field_errors }} in the template. If used on its own, it
# is displayed as a <ul> (or an empty string, if the list of errors is
# empty).
t = Template(
'<form>'
'{{ form.username.errors.as_ul }}'
'<p><label>Your username: {{ form.username }}</label></p>'
'{{ form.password1.errors.as_ul }}'
'<p><label>Password: {{ form.password1 }}</label></p>'
'{{ form.password2.errors.as_ul }}'
'<p><label>Password (again): {{ form.password2 }}</label></p>'
'<input type="submit" required>'
'</form>'
)
f = UserRegistration(
{'username': 'django', 'password1': 'foo', 'password2': 'bar'},
auto_id=False,
)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<p><label>Your username: '
'<input type="text" name="username" value="django" maxlength="10" required>'
'</label></p>'
'<p><label>Password: '
'<input type="password" name="password1" required></label></p>'
'<p><label>Password (again): '
'<input type="password" name="password2" required></label></p>'
'<input type="submit" required>'
'</form>',
)
t = Template(
'<form>'
'{{ form.non_field_errors }}'
'{{ form.username.errors.as_ul }}'
'<p><label>Your username: {{ form.username }}</label></p>'
'{{ form.password1.errors.as_ul }}'
'<p><label>Password: {{ form.password1 }}</label></p>'
'{{ form.password2.errors.as_ul }}'
'<p><label>Password (again): {{ form.password2 }}</label></p>'
'<input type="submit" required>'
'</form>'
)
self.assertHTMLEqual(
t.render(Context({'form': f})),
'<form>'
'<ul class="errorlist nonfield">'
'<li>Please make sure your passwords match.</li></ul>'
'<p><label>Your username: '
'<input type="text" name="username" value="django" maxlength="10" required>'
'</label></p>'
'<p><label>Password: '
'<input type="password" name="password1" required></label></p>'
'<p><label>Password (again): '
'<input type="password" name="password2" required></label></p>'
'<input type="submit" required>'
'</form>',
)
def test_basic_processing_in_view(self):
class UserRegistration(Form):
username = CharField(max_length=10)
password1 = CharField(widget=PasswordInput)
password2 = CharField(widget=PasswordInput)
def clean(self):
if (
self.cleaned_data.get('password1') and
self.cleaned_data.get('password2') and
self.cleaned_data['password1'] != self.cleaned_data['password2']
):
raise ValidationError('Please make sure your passwords match.')
return self.cleaned_data
def my_function(method, post_data):
if method == 'POST':
form = UserRegistration(post_data, auto_id=False)
else:
form = UserRegistration(auto_id=False)
if form.is_valid():
return 'VALID: %r' % sorted(form.cleaned_data.items())
t = Template(
'<form method="post">'
'<table>'
'{{ form }}'
'</table>'
'<input type="submit" required>'
'</form>'
)
return t.render(Context({'form': form}))
# GET with an empty form and no errors.
self.assertHTMLEqual(
my_function('GET', {}),
'<form method="post">'
'<table>'
'<tr><th>Username:</th><td>'
'<input type="text" name="username" maxlength="10" required></td></tr>'
'<tr><th>Password1:</th><td>'
'<input type="password" name="password1" required></td></tr>'
'<tr><th>Password2:</th><td>'
'<input type="password" name="password2" required></td></tr>'
'</table>'
'<input type="submit" required>'
'</form>',
)
# POST with erroneous data, a redisplayed form, with errors.
self.assertHTMLEqual(
my_function('POST', {
'username': 'this-is-a-long-username',
'password1': 'foo',
'password2': 'bar',
}),
'<form method="post">'
'<table>'
'<tr><td colspan="2"><ul class="errorlist nonfield">'
'<li>Please make sure your passwords match.</li></ul></td></tr>'
'<tr><th>Username:</th><td><ul class="errorlist">'
'<li>Ensure this value has at most 10 characters (it has 23).</li></ul>'
'<input type="text" name="username" '
'value="this-is-a-long-username" maxlength="10" required></td></tr>'
'<tr><th>Password1:</th><td>'
'<input type="password" name="password1" required></td></tr>'
'<tr><th>Password2:</th><td>'
'<input type="password" name="password2" required></td></tr>'
'</table>'
'<input type="submit" required>'
'</form>',
)
# POST with valid data (the success message).
self.assertEqual(
my_function('POST', {
'username': 'adrian',
'password1': 'secret',
'password2': 'secret',
}),
"VALID: [('password1', 'secret'), ('password2', 'secret'), "
"('username', 'adrian')]",
)