Rewrote form widget tests as proper unittests.
This is preparation for landing the template-based widget rendering patch and goes a long way to making these tests more useful for future development. The old doctest heritage is strong here.
This commit is contained in:
parent
5153a3bfdc
commit
4c30fa905d
|
@ -0,0 +1,111 @@
|
|||
from datetime import date
|
||||
|
||||
from django.forms import DateField, Form, HiddenInput, SelectDateWidget
|
||||
from django.test import SimpleTestCase, override_settings
|
||||
from django.utils import translation
|
||||
|
||||
|
||||
class GetDate(Form):
|
||||
mydate = DateField(widget=SelectDateWidget)
|
||||
|
||||
|
||||
class DateFieldTest(SimpleTestCase):
|
||||
|
||||
def test_form_field(self):
|
||||
a = GetDate({'mydate_month': '4', 'mydate_day': '1', 'mydate_year': '2008'})
|
||||
self.assertTrue(a.is_valid())
|
||||
self.assertEqual(a.cleaned_data['mydate'], date(2008, 4, 1))
|
||||
|
||||
# As with any widget that implements get_value_from_datadict(), we must
|
||||
# accept the input from the "as_hidden" rendering as well.
|
||||
self.assertHTMLEqual(
|
||||
a['mydate'].as_hidden(),
|
||||
'<input type="hidden" name="mydate" value="2008-4-1" id="id_mydate" />',
|
||||
)
|
||||
|
||||
b = GetDate({'mydate': '2008-4-1'})
|
||||
self.assertTrue(b.is_valid())
|
||||
self.assertEqual(b.cleaned_data['mydate'], date(2008, 4, 1))
|
||||
|
||||
# Invalid dates shouldn't be allowed
|
||||
c = GetDate({'mydate_month': '2', 'mydate_day': '31', 'mydate_year': '2010'})
|
||||
self.assertFalse(c.is_valid())
|
||||
self.assertEqual(c.errors, {'mydate': ['Enter a valid date.']})
|
||||
|
||||
# label tag is correctly associated with month dropdown
|
||||
d = GetDate({'mydate_month': '1', 'mydate_day': '1', 'mydate_year': '2010'})
|
||||
self.assertIn('<label for="id_mydate_month">', d.as_p())
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('nl')
|
||||
def test_l10n_date_changed(self):
|
||||
"""
|
||||
Ensure that DateField.has_changed() with SelectDateWidget works
|
||||
correctly with a localized date format (#17165).
|
||||
"""
|
||||
# With Field.show_hidden_initial=False
|
||||
b = GetDate({
|
||||
'mydate_year': '2008',
|
||||
'mydate_month': '4',
|
||||
'mydate_day': '1',
|
||||
}, initial={'mydate': date(2008, 4, 1)})
|
||||
self.assertFalse(b.has_changed())
|
||||
|
||||
b = GetDate({
|
||||
'mydate_year': '2008',
|
||||
'mydate_month': '4',
|
||||
'mydate_day': '2',
|
||||
}, initial={'mydate': date(2008, 4, 1)})
|
||||
self.assertTrue(b.has_changed())
|
||||
|
||||
# With Field.show_hidden_initial=True
|
||||
class GetDateShowHiddenInitial(Form):
|
||||
mydate = DateField(widget=SelectDateWidget, show_hidden_initial=True)
|
||||
|
||||
b = GetDateShowHiddenInitial({
|
||||
'mydate_year': '2008',
|
||||
'mydate_month': '4',
|
||||
'mydate_day': '1',
|
||||
'initial-mydate': HiddenInput()._format_value(date(2008, 4, 1)),
|
||||
}, initial={'mydate': date(2008, 4, 1)})
|
||||
self.assertFalse(b.has_changed())
|
||||
|
||||
b = GetDateShowHiddenInitial({
|
||||
'mydate_year': '2008',
|
||||
'mydate_month': '4',
|
||||
'mydate_day': '22',
|
||||
'initial-mydate': HiddenInput()._format_value(date(2008, 4, 1)),
|
||||
}, initial={'mydate': date(2008, 4, 1)})
|
||||
self.assertTrue(b.has_changed())
|
||||
|
||||
b = GetDateShowHiddenInitial({
|
||||
'mydate_year': '2008',
|
||||
'mydate_month': '4',
|
||||
'mydate_day': '22',
|
||||
'initial-mydate': HiddenInput()._format_value(date(2008, 4, 1)),
|
||||
}, initial={'mydate': date(2008, 4, 22)})
|
||||
self.assertTrue(b.has_changed())
|
||||
|
||||
b = GetDateShowHiddenInitial({
|
||||
'mydate_year': '2008',
|
||||
'mydate_month': '4',
|
||||
'mydate_day': '22',
|
||||
'initial-mydate': HiddenInput()._format_value(date(2008, 4, 22)),
|
||||
}, initial={'mydate': date(2008, 4, 1)})
|
||||
self.assertFalse(b.has_changed())
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('nl')
|
||||
def test_l10n_invalid_date_in(self):
|
||||
# Invalid dates shouldn't be allowed
|
||||
a = GetDate({'mydate_month': '2', 'mydate_day': '31', 'mydate_year': '2010'})
|
||||
self.assertFalse(a.is_valid())
|
||||
# 'Geef een geldige datum op.' = 'Enter a valid date.'
|
||||
self.assertEqual(a.errors, {'mydate': ['Geef een geldige datum op.']})
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('nl')
|
||||
def test_form_label_association(self):
|
||||
# label tag is correctly associated with first rendered dropdown
|
||||
a = GetDate({'mydate_month': '1', 'mydate_day': '1', 'mydate_year': '2010'})
|
||||
self.assertIn('<label for="id_mydate_day">', a.as_p())
|
|
@ -0,0 +1,162 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.forms import (
|
||||
CharField, Form, MultipleChoiceField, MultiValueField, MultiWidget,
|
||||
SelectMultiple, SplitDateTimeField, SplitDateTimeWidget, TextInput,
|
||||
ValidationError,
|
||||
)
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
beatles = (('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))
|
||||
|
||||
|
||||
class ComplexMultiWidget(MultiWidget):
|
||||
def __init__(self, attrs=None):
|
||||
widgets = (
|
||||
TextInput(),
|
||||
SelectMultiple(choices=beatles),
|
||||
SplitDateTimeWidget(),
|
||||
)
|
||||
super(ComplexMultiWidget, self).__init__(widgets, attrs)
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
data = value.split(',')
|
||||
return [
|
||||
data[0],
|
||||
list(data[1]),
|
||||
datetime.strptime(data[2], "%Y-%m-%d %H:%M:%S"),
|
||||
]
|
||||
return [None, None, None]
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
return '\n'.join(rendered_widgets)
|
||||
|
||||
|
||||
class ComplexField(MultiValueField):
|
||||
def __init__(self, required=True, widget=None, label=None, initial=None):
|
||||
fields = (
|
||||
CharField(),
|
||||
MultipleChoiceField(choices=beatles),
|
||||
SplitDateTimeField(),
|
||||
)
|
||||
super(ComplexField, self).__init__(fields, required, widget, label, initial)
|
||||
|
||||
def compress(self, data_list):
|
||||
if data_list:
|
||||
return '%s,%s,%s' % (data_list[0], ''.join(data_list[1]), data_list[2])
|
||||
return None
|
||||
|
||||
|
||||
class ComplexFieldForm(Form):
|
||||
field1 = ComplexField(widget=ComplexMultiWidget())
|
||||
|
||||
|
||||
class MultiValueFieldTest(SimpleTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.field = ComplexField(widget=ComplexMultiWidget())
|
||||
super(MultiValueFieldTest, cls).setUpClass()
|
||||
|
||||
def test_clean(self):
|
||||
self.assertEqual(
|
||||
self.field.clean(['some text', ['J', 'P'], ['2007-04-25', '6:24:00']]),
|
||||
'some text,JP,2007-04-25 06:24:00',
|
||||
)
|
||||
|
||||
def test_bad_choice(self):
|
||||
msg = "'Select a valid choice. X is not one of the available choices.'"
|
||||
with self.assertRaisesMessage(ValidationError, msg):
|
||||
self.field.clean(['some text', ['X'], ['2007-04-25', '6:24:00']])
|
||||
|
||||
def test_no_value(self):
|
||||
"""
|
||||
If insufficient data is provided, None is substituted.
|
||||
"""
|
||||
msg = "'This field is required.'"
|
||||
with self.assertRaisesMessage(ValidationError, msg):
|
||||
self.field.clean(['some text', ['JP']])
|
||||
|
||||
def test_has_changed_no_initial(self):
|
||||
self.assertTrue(self.field.has_changed(
|
||||
None, ['some text', ['J', 'P'], ['2007-04-25', '6:24:00']],
|
||||
))
|
||||
|
||||
def test_has_changed_same(self):
|
||||
self.assertFalse(self.field.has_changed(
|
||||
'some text,JP,2007-04-25 06:24:00',
|
||||
['some text', ['J', 'P'], ['2007-04-25', '6:24:00']],
|
||||
))
|
||||
|
||||
def test_has_changed_first_widget(self):
|
||||
"""
|
||||
Test when the first widget's data has changed.
|
||||
"""
|
||||
self.assertTrue(self.field.has_changed(
|
||||
'some text,JP,2007-04-25 06:24:00',
|
||||
['other text', ['J', 'P'], ['2007-04-25', '6:24:00']],
|
||||
))
|
||||
|
||||
def test_has_changed_last_widget(self):
|
||||
"""
|
||||
Test when the last widget's data has changed. This ensures that it is
|
||||
not short circuiting while testing the widgets.
|
||||
"""
|
||||
self.assertTrue(self.field.has_changed(
|
||||
'some text,JP,2007-04-25 06:24:00',
|
||||
['some text', ['J', 'P'], ['2009-04-25', '11:44:00']],
|
||||
))
|
||||
|
||||
def test_form_as_table(self):
|
||||
form = ComplexFieldForm()
|
||||
self.assertHTMLEqual(
|
||||
form.as_table(),
|
||||
"""
|
||||
<tr><th><label for="id_field1_0">Field1:</label></th>
|
||||
<td><input type="text" name="field1_0" id="id_field1_0" />
|
||||
<select multiple="multiple" name="field1_1" id="id_field1_1">
|
||||
<option value="J">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>
|
||||
<input type="text" name="field1_2_0" id="id_field1_2_0" />
|
||||
<input type="text" name="field1_2_1" id="id_field1_2_1" /></td></tr>
|
||||
""",
|
||||
)
|
||||
|
||||
def test_form_as_table_data(self):
|
||||
form = ComplexFieldForm({
|
||||
'field1_0': 'some text',
|
||||
'field1_1': ['J', 'P'],
|
||||
'field1_2_0': '2007-04-25',
|
||||
'field1_2_1': '06:24:00',
|
||||
})
|
||||
self.assertHTMLEqual(
|
||||
form.as_table(),
|
||||
"""
|
||||
<tr><th><label for="id_field1_0">Field1:</label></th>
|
||||
<td><input type="text" name="field1_0" value="some text" id="id_field1_0" />
|
||||
<select multiple="multiple" name="field1_1" id="id_field1_1">
|
||||
<option value="J" selected="selected">John</option>
|
||||
<option value="P" selected="selected">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>
|
||||
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" />
|
||||
<input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr>
|
||||
""",
|
||||
)
|
||||
|
||||
def test_form_cleaned_data(self):
|
||||
form = ComplexFieldForm({
|
||||
'field1_0': 'some text',
|
||||
'field1_1': ['J', 'P'],
|
||||
'field1_2_0': '2007-04-25',
|
||||
'field1_2_1': '06:24:00',
|
||||
})
|
||||
form.is_valid()
|
||||
self.assertEqual(
|
||||
form.cleaned_data['field1'], 'some text,JP,2007-04-25 06:24:00',
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
from django.test import SimpleTestCase
|
||||
|
||||
|
||||
class WidgetTest(SimpleTestCase):
|
||||
beatles = (('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))
|
||||
|
||||
def check_html(self, widget, name, value, html='', attrs=None, **kwargs):
|
||||
output = widget.render(name, value, attrs=attrs, **kwargs)
|
||||
self.assertHTMLEqual(output, html)
|
|
@ -0,0 +1,87 @@
|
|||
from django.forms import CheckboxInput
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class CheckboxInputTest(WidgetTest):
|
||||
widget = CheckboxInput()
|
||||
|
||||
def test_render_empty(self):
|
||||
self.check_html(self.widget, 'is_cool', '', html='<input type="checkbox" name="is_cool" />')
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'is_cool', None, html='<input type="checkbox" name="is_cool" />')
|
||||
|
||||
def test_render_false(self):
|
||||
self.check_html(self.widget, 'is_cool', False, html='<input type="checkbox" name="is_cool" />')
|
||||
|
||||
def test_render_true(self):
|
||||
self.check_html(
|
||||
self.widget, 'is_cool', True,
|
||||
html='<input checked="checked" type="checkbox" name="is_cool" />'
|
||||
)
|
||||
|
||||
def test_render_value(self):
|
||||
"""
|
||||
Using any value that's not in ('', None, False, True) will check the
|
||||
checkbox and set the 'value' attribute.
|
||||
"""
|
||||
self.check_html(
|
||||
self.widget, 'is_cool', 'foo',
|
||||
html='<input checked="checked" type="checkbox" name="is_cool" value="foo" />',
|
||||
)
|
||||
|
||||
def test_render_int(self):
|
||||
"""
|
||||
Integers are handled by value, not as booleans (#17114).
|
||||
"""
|
||||
self.check_html(
|
||||
self.widget, 'is_cool', 0,
|
||||
html='<input checked="checked" type="checkbox" name="is_cool" value="0" />',
|
||||
)
|
||||
self.check_html(
|
||||
self.widget, 'is_cool', 1,
|
||||
html='<input checked="checked" type="checkbox" name="is_cool" value="1" />',
|
||||
)
|
||||
|
||||
def test_render_check_test(self):
|
||||
"""
|
||||
You can pass 'check_test' to the constructor. This is a callable that
|
||||
takes the value and returns True if the box should be checked.
|
||||
"""
|
||||
widget = CheckboxInput(check_test=lambda value: value.startswith('hello'))
|
||||
self.check_html(widget, 'greeting', '', html=(
|
||||
'<input type="checkbox" name="greeting" />'
|
||||
))
|
||||
self.check_html(widget, 'greeting', 'hello', html=(
|
||||
'<input checked="checked" type="checkbox" name="greeting" value="hello" />'
|
||||
))
|
||||
self.check_html(widget, 'greeting', 'hello there', html=(
|
||||
'<input checked="checked" type="checkbox" name="greeting" value="hello there" />'
|
||||
))
|
||||
self.check_html(widget, 'greeting', 'hello & goodbye', html=(
|
||||
'<input checked="checked" type="checkbox" name="greeting" value="hello & goodbye" />'
|
||||
))
|
||||
|
||||
def test_render_check_exception(self):
|
||||
"""
|
||||
Calling check_test() shouldn't swallow exceptions (#17888).
|
||||
"""
|
||||
widget = CheckboxInput(
|
||||
check_test=lambda value: value.startswith('hello'),
|
||||
)
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
widget.render('greeting', True)
|
||||
|
||||
def test_value_from_datadict(self):
|
||||
"""
|
||||
The CheckboxInput widget will return False if the key is not found in
|
||||
the data dictionary (because HTML form submission doesn't send any
|
||||
result for unchecked checkboxes).
|
||||
"""
|
||||
self.assertFalse(self.widget.value_from_datadict({}, {}, 'testing'))
|
||||
|
||||
def test_value_from_datadict_string_int(self):
|
||||
value = self.widget.value_from_datadict({'testing': '0'}, {}, 'testing')
|
||||
self.assertEqual(value, True)
|
|
@ -0,0 +1,115 @@
|
|||
from django.forms import CheckboxSelectMultiple
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class CheckboxSelectMultipleTest(WidgetTest):
|
||||
widget = CheckboxSelectMultiple()
|
||||
|
||||
def test_render_value(self):
|
||||
self.check_html(self.widget, 'beatles', ['J'], choices=self.beatles, html=(
|
||||
"""<ul>
|
||||
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
|
||||
</ul>"""
|
||||
))
|
||||
|
||||
def test_render_value_multiple(self):
|
||||
self.check_html(self.widget, 'beatles', ['J', 'P'], choices=self.beatles, html=(
|
||||
"""<ul>
|
||||
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
|
||||
<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
|
||||
</ul>"""
|
||||
))
|
||||
|
||||
def test_render_none(self):
|
||||
"""
|
||||
If the value is None, none of the options are selected.
|
||||
"""
|
||||
self.check_html(self.widget, 'beatles', None, choices=self.beatles, html=(
|
||||
"""<ul>
|
||||
<li><label><input type="checkbox" name="beatles" value="J" /> John</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
|
||||
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
|
||||
</ul>"""
|
||||
))
|
||||
|
||||
def test_nested_choices(self):
|
||||
nested_choices = (
|
||||
('unknown', 'Unknown'),
|
||||
('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))),
|
||||
('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
|
||||
)
|
||||
html = """
|
||||
<ul id="media">
|
||||
<li>
|
||||
<label for="media_0"><input id="media_0" name="nestchoice" type="checkbox" value="unknown" /> Unknown</label>
|
||||
</li>
|
||||
<li>Audio<ul id="media_1">
|
||||
<li>
|
||||
<label for="media_1_0">
|
||||
<input checked="checked" id="media_1_0" name="nestchoice" type="checkbox" value="vinyl" /> Vinyl
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label for="media_1_1"><input id="media_1_1" name="nestchoice" type="checkbox" value="cd" /> CD</label>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>Video<ul id="media_2">
|
||||
<li>
|
||||
<label for="media_2_0"><input id="media_2_0" name="nestchoice" type="checkbox" value="vhs" /> VHS</label>
|
||||
</li>
|
||||
<li>
|
||||
<label for="media_2_1">
|
||||
<input checked="checked" id="media_2_1" name="nestchoice" type="checkbox" value="dvd" /> DVD
|
||||
</label>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
"""
|
||||
self.check_html(
|
||||
self.widget, 'nestchoice', ('vinyl', 'dvd'),
|
||||
choices=nested_choices, attrs={'id': 'media'}, html=html,
|
||||
)
|
||||
|
||||
def test_separate_ids(self):
|
||||
"""
|
||||
Each input gets a separate ID.
|
||||
"""
|
||||
choices = [('a', 'A'), ('b', 'B'), ('c', 'C')]
|
||||
html = """
|
||||
<ul id="abc">
|
||||
<li>
|
||||
<label for="abc_0"><input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" /> A</label>
|
||||
</li>
|
||||
<li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1" /> B</label></li>
|
||||
<li>
|
||||
<label for="abc_2"><input checked="checked" type="checkbox" name="letters" value="c" id="abc_2" /> C</label>
|
||||
</li>
|
||||
</ul>
|
||||
"""
|
||||
self.check_html(self.widget, 'letters', ['a', 'c'], choices=choices, attrs={'id': 'abc'}, html=html)
|
||||
|
||||
def test_separate_ids_constructor(self):
|
||||
"""
|
||||
Each input gets a separate ID when the ID is passed to the constructor.
|
||||
"""
|
||||
widget = CheckboxSelectMultiple(attrs={'id': 'abc'})
|
||||
choices = [('a', 'A'), ('b', 'B'), ('c', 'C')]
|
||||
html = """
|
||||
<ul id="abc">
|
||||
<li>
|
||||
<label for="abc_0"><input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" /> A</label>
|
||||
</li>
|
||||
<li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1" /> B</label></li>
|
||||
<li>
|
||||
<label for="abc_2"><input checked="checked" type="checkbox" name="letters" value="c" id="abc_2" /> C</label>
|
||||
</li>
|
||||
</ul>
|
||||
"""
|
||||
self.check_html(widget, 'letters', ['a', 'c'], choices=choices, html=html)
|
|
@ -0,0 +1,126 @@
|
|||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.forms import ClearableFileInput
|
||||
from django.utils import six
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class FakeFieldFile(object):
|
||||
"""
|
||||
Quacks like a FieldFile (has a .url and unicode representation), but
|
||||
doesn't require us to care about storages etc.
|
||||
"""
|
||||
url = 'something'
|
||||
|
||||
def __str__(self):
|
||||
return self.url
|
||||
|
||||
|
||||
class ClearableFileInputTest(WidgetTest):
|
||||
widget = ClearableFileInput()
|
||||
|
||||
def test_clear_input_renders(self):
|
||||
"""
|
||||
A ClearableFileInput with is_required False and rendered with an
|
||||
initial value that is a file renders a clear checkbox.
|
||||
"""
|
||||
self.check_html(self.widget, 'myfile', FakeFieldFile(), html=(
|
||||
"""
|
||||
Currently: <a href="something">something</a>
|
||||
<input type="checkbox" name="myfile-clear" id="myfile-clear_id" />
|
||||
<label for="myfile-clear_id">Clear</label><br />
|
||||
Change: <input type="file" name="myfile" />
|
||||
"""
|
||||
))
|
||||
|
||||
def test_html_escaped(self):
|
||||
"""
|
||||
A ClearableFileInput should escape name, filename, and URL
|
||||
when rendering HTML (#15182).
|
||||
"""
|
||||
@python_2_unicode_compatible
|
||||
class StrangeFieldFile(object):
|
||||
url = "something?chapter=1§=2©=3&lang=en"
|
||||
|
||||
def __str__(self):
|
||||
return '''something<div onclick="alert('oops')">.jpg'''
|
||||
|
||||
widget = ClearableFileInput()
|
||||
field = StrangeFieldFile()
|
||||
output = widget.render('my<div>file', field)
|
||||
self.assertNotIn(field.url, output)
|
||||
self.assertIn('href="something?chapter=1&sect=2&copy=3&lang=en"', output)
|
||||
self.assertNotIn(six.text_type(field), output)
|
||||
self.assertIn('something<div onclick="alert('oops')">.jpg', output)
|
||||
self.assertIn('my<div>file', output)
|
||||
self.assertNotIn('my<div>file', output)
|
||||
|
||||
def test_html_does_not_mask_exceptions(self):
|
||||
"""
|
||||
A ClearableFileInput should not mask exceptions produced while
|
||||
checking that it has a value.
|
||||
"""
|
||||
@python_2_unicode_compatible
|
||||
class FailingURLFieldFile(object):
|
||||
@property
|
||||
def url(self):
|
||||
raise RuntimeError('Canary')
|
||||
|
||||
def __str__(self):
|
||||
return 'value'
|
||||
|
||||
widget = ClearableFileInput()
|
||||
field = FailingURLFieldFile()
|
||||
with self.assertRaisesMessage(RuntimeError, 'Canary'):
|
||||
widget.render('myfile', field)
|
||||
|
||||
def test_clear_input_renders_only_if_not_required(self):
|
||||
"""
|
||||
A ClearableFileInput with is_required=False does not render a clear
|
||||
checkbox.
|
||||
"""
|
||||
widget = ClearableFileInput()
|
||||
widget.is_required = True
|
||||
self.check_html(widget, 'myfile', FakeFieldFile(), html=(
|
||||
"""
|
||||
Currently: <a href="something">something</a> <br />
|
||||
Change: <input type="file" name="myfile" />
|
||||
"""
|
||||
))
|
||||
|
||||
def test_clear_input_renders_only_if_initial(self):
|
||||
"""
|
||||
A ClearableFileInput instantiated with no initial value does not render
|
||||
a clear checkbox.
|
||||
"""
|
||||
self.check_html(self.widget, 'myfile', None, html='<input type="file" name="myfile" />')
|
||||
|
||||
def test_clear_input_checked_returns_false(self):
|
||||
"""
|
||||
ClearableFileInput.value_from_datadict returns False if the clear
|
||||
checkbox is checked, if not required.
|
||||
"""
|
||||
value = self.widget.value_from_datadict(
|
||||
data={'myfile-clear': True},
|
||||
files={},
|
||||
name='myfile',
|
||||
)
|
||||
self.assertEqual(value, False)
|
||||
|
||||
def test_clear_input_checked_returns_false_only_if_not_required(self):
|
||||
"""
|
||||
ClearableFileInput.value_from_datadict never returns False if the field
|
||||
is required.
|
||||
"""
|
||||
widget = ClearableFileInput()
|
||||
widget.is_required = True
|
||||
field = SimpleUploadedFile('something.txt', b'content')
|
||||
|
||||
value = widget.value_from_datadict(
|
||||
data={'myfile-clear': True},
|
||||
files={'myfile': field},
|
||||
name='myfile',
|
||||
)
|
||||
self.assertEqual(value, field)
|
|
@ -0,0 +1,47 @@
|
|||
from datetime import date
|
||||
|
||||
from django.forms import DateInput
|
||||
from django.test import override_settings
|
||||
from django.utils import translation
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class DateInputTest(WidgetTest):
|
||||
widget = DateInput()
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'date', None, html='<input type="text" name="date" />')
|
||||
|
||||
def test_render_value(self):
|
||||
d = date(2007, 9, 17)
|
||||
self.assertEqual(str(d), '2007-09-17')
|
||||
|
||||
self.check_html(self.widget, 'date', d, html='<input type="text" name="date" value="2007-09-17" />')
|
||||
self.check_html(self.widget, 'date', date(2007, 9, 17), html=(
|
||||
'<input type="text" name="date" value="2007-09-17" />'
|
||||
))
|
||||
|
||||
def test_string(self):
|
||||
"""
|
||||
Should be able to initialize from a string value.
|
||||
"""
|
||||
self.check_html(self.widget, 'date', '2007-09-17', html=(
|
||||
'<input type="text" name="date" value="2007-09-17" />'
|
||||
))
|
||||
|
||||
def test_format(self):
|
||||
"""
|
||||
Use 'format' to change the way a value is displayed.
|
||||
"""
|
||||
d = date(2007, 9, 17)
|
||||
widget = DateInput(format='%d/%m/%Y', attrs={'type': 'date'})
|
||||
self.check_html(widget, 'date', d, html='<input type="date" name="date" value="17/09/2007" />')
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('de-at')
|
||||
def test_l10n(self):
|
||||
self.check_html(
|
||||
self.widget, 'date', date(2007, 9, 17),
|
||||
html='<input type="text" name="date" value="17.09.2007" />',
|
||||
)
|
|
@ -0,0 +1,63 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.forms import DateTimeInput
|
||||
from django.test import override_settings
|
||||
from django.utils import translation
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class DateTimeInputTest(WidgetTest):
|
||||
widget = DateTimeInput()
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'date', None, '<input type="text" name="date" />')
|
||||
|
||||
def test_render_value(self):
|
||||
"""
|
||||
The microseconds are trimmed on display, by default.
|
||||
"""
|
||||
d = datetime(2007, 9, 17, 12, 51, 34, 482548)
|
||||
self.assertEqual(str(d), '2007-09-17 12:51:34.482548')
|
||||
self.check_html(self.widget, 'date', d, html=(
|
||||
'<input type="text" name="date" value="2007-09-17 12:51:34" />'
|
||||
))
|
||||
self.check_html(self.widget, 'date', datetime(2007, 9, 17, 12, 51, 34), html=(
|
||||
'<input type="text" name="date" value="2007-09-17 12:51:34" />'
|
||||
))
|
||||
self.check_html(self.widget, 'date', datetime(2007, 9, 17, 12, 51), html=(
|
||||
'<input type="text" name="date" value="2007-09-17 12:51:00" />'
|
||||
))
|
||||
|
||||
def test_render_formatted(self):
|
||||
"""
|
||||
Use 'format' to change the way a value is displayed.
|
||||
"""
|
||||
widget = DateTimeInput(
|
||||
format='%d/%m/%Y %H:%M', attrs={'type': 'datetime'},
|
||||
)
|
||||
d = datetime(2007, 9, 17, 12, 51, 34, 482548)
|
||||
self.check_html(widget, 'date', d, html='<input type="datetime" name="date" value="17/09/2007 12:51" />')
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('de-at')
|
||||
def test_l10n(self):
|
||||
d = datetime(2007, 9, 17, 12, 51, 34, 482548)
|
||||
self.check_html(self.widget, 'date', d, html=(
|
||||
'<input type="text" name="date" value="17.09.2007 12:51:34" />'
|
||||
))
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('de-at')
|
||||
def test_locale_aware(self):
|
||||
d = datetime(2007, 9, 17, 12, 51, 34, 482548)
|
||||
with self.settings(USE_L10N=False):
|
||||
self.check_html(
|
||||
self.widget, 'date', d,
|
||||
html='<input type="text" name="date" value="2007-09-17 12:51:34" />',
|
||||
)
|
||||
with translation.override('es'):
|
||||
self.check_html(
|
||||
self.widget, 'date', d,
|
||||
html='<input type="text" name="date" value="17/09/2007 12:51:34" />',
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
from django.forms import FileInput
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class FileInputTest(WidgetTest):
|
||||
widget = FileInput()
|
||||
|
||||
def test_render(self):
|
||||
"""
|
||||
FileInput widgets never render the value attribute. The old value
|
||||
isn't useful if a form is updated or an error occurred.
|
||||
"""
|
||||
self.check_html(self.widget, 'email', 'test@example.com', html='<input type="file" name="email" />')
|
||||
self.check_html(self.widget, 'email', '', html='<input type="file" name="email" />')
|
||||
self.check_html(self.widget, 'email', None, html='<input type="file" name="email" />')
|
|
@ -0,0 +1,10 @@
|
|||
from django.forms import HiddenInput
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class HiddenInputTest(WidgetTest):
|
||||
widget = HiddenInput()
|
||||
|
||||
def test_render(self):
|
||||
self.check_html(self.widget, 'email', '', html='<input type="hidden" name="email" />')
|
|
@ -0,0 +1,75 @@
|
|||
from django.forms import MultipleHiddenInput
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class MultipleHiddenInputTest(WidgetTest):
|
||||
widget = MultipleHiddenInput()
|
||||
|
||||
def test_render_single(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', ['test@example.com'],
|
||||
html='<input type="hidden" name="email" value="test@example.com" />',
|
||||
)
|
||||
|
||||
def test_render_multiple(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', ['test@example.com', 'foo@example.com'],
|
||||
html=(
|
||||
'<input type="hidden" name="email" value="test@example.com" />\n'
|
||||
'<input type="hidden" name="email" value="foo@example.com" />'
|
||||
),
|
||||
)
|
||||
|
||||
def test_render_attrs(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', ['test@example.com'], attrs={'class': 'fun'},
|
||||
html='<input type="hidden" name="email" value="test@example.com" class="fun" />',
|
||||
)
|
||||
|
||||
def test_render_attrs_multiple(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'},
|
||||
html=(
|
||||
'<input type="hidden" name="email" value="test@example.com" class="fun" />\n'
|
||||
'<input type="hidden" name="email" value="foo@example.com" class="fun" />'
|
||||
),
|
||||
)
|
||||
|
||||
def test_render_attrs_constructor(self):
|
||||
widget = MultipleHiddenInput(attrs={'class': 'fun'})
|
||||
self.check_html(widget, 'email', [], '')
|
||||
self.check_html(
|
||||
widget, 'email', ['foo@example.com'],
|
||||
html='<input type="hidden" class="fun" value="foo@example.com" name="email" />',
|
||||
)
|
||||
self.check_html(
|
||||
widget, 'email', ['foo@example.com', 'test@example.com'],
|
||||
html=(
|
||||
'<input type="hidden" class="fun" value="foo@example.com" name="email" />\n'
|
||||
'<input type="hidden" class="fun" value="test@example.com" name="email" />'
|
||||
),
|
||||
)
|
||||
self.check_html(
|
||||
widget, 'email', ['foo@example.com'], attrs={'class': 'special'},
|
||||
html='<input type="hidden" class="special" value="foo@example.com" name="email" />',
|
||||
)
|
||||
|
||||
def test_render_empty(self):
|
||||
self.check_html(self.widget, 'email', [], '')
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'email', None, '')
|
||||
|
||||
def test_render_increment_id(self):
|
||||
"""
|
||||
Each input should get a separate ID.
|
||||
"""
|
||||
self.check_html(
|
||||
self.widget, 'letters', ['a', 'b', 'c'], attrs={'id': 'hideme'},
|
||||
html=(
|
||||
'<input type="hidden" name="letters" value="a" id="hideme_0" />\n'
|
||||
'<input type="hidden" name="letters" value="b" id="hideme_1" />\n'
|
||||
'<input type="hidden" name="letters" value="c" id="hideme_2" />'
|
||||
),
|
||||
)
|
|
@ -0,0 +1,163 @@
|
|||
import copy
|
||||
from datetime import datetime
|
||||
|
||||
from django.forms import (
|
||||
CharField, FileInput, MultipleChoiceField, MultiValueField, MultiWidget,
|
||||
RadioSelect, SelectMultiple, SplitDateTimeField, SplitDateTimeWidget,
|
||||
TextInput,
|
||||
)
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class MyMultiWidget(MultiWidget):
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
return value.split('__')
|
||||
return ['', '']
|
||||
|
||||
|
||||
class ComplexMultiWidget(MultiWidget):
|
||||
def __init__(self, attrs=None):
|
||||
widgets = (
|
||||
TextInput(),
|
||||
SelectMultiple(choices=WidgetTest.beatles),
|
||||
SplitDateTimeWidget(),
|
||||
)
|
||||
super(ComplexMultiWidget, self).__init__(widgets, attrs)
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
data = value.split(',')
|
||||
return [
|
||||
data[0], list(data[1]), datetime.strptime(data[2], "%Y-%m-%d %H:%M:%S")
|
||||
]
|
||||
return [None, None, None]
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
return '\n'.join(rendered_widgets)
|
||||
|
||||
|
||||
class ComplexField(MultiValueField):
|
||||
def __init__(self, required=True, widget=None, label=None, initial=None):
|
||||
fields = (
|
||||
CharField(),
|
||||
MultipleChoiceField(choices=WidgetTest.beatles),
|
||||
SplitDateTimeField(),
|
||||
)
|
||||
super(ComplexField, self).__init__(
|
||||
fields, required, widget, label, initial,
|
||||
)
|
||||
|
||||
def compress(self, data_list):
|
||||
if data_list:
|
||||
return '%s,%s,%s' % (
|
||||
data_list[0], ''.join(data_list[1]), data_list[2],
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
class DeepCopyWidget(MultiWidget):
|
||||
"""
|
||||
Used to test MultiWidget.__deepcopy__().
|
||||
"""
|
||||
def __init__(self, choices=[]):
|
||||
widgets = [
|
||||
RadioSelect(choices=choices),
|
||||
TextInput,
|
||||
]
|
||||
super(DeepCopyWidget, self).__init__(widgets)
|
||||
|
||||
def _set_choices(self, choices):
|
||||
"""
|
||||
When choices are set for this widget, we want to pass those along to
|
||||
the Select widget.
|
||||
"""
|
||||
self.widgets[0].choices = choices
|
||||
|
||||
def _get_choices(self):
|
||||
"""
|
||||
The choices for this widget are the Select widget's choices.
|
||||
"""
|
||||
return self.widgets[0].choices
|
||||
choices = property(_get_choices, _set_choices)
|
||||
|
||||
|
||||
class MultiWidgetTest(WidgetTest):
|
||||
|
||||
def test_text_inputs(self):
|
||||
widget = MyMultiWidget(
|
||||
widgets=(
|
||||
TextInput(attrs={'class': 'big'}),
|
||||
TextInput(attrs={'class': 'small'}),
|
||||
)
|
||||
)
|
||||
self.check_html(widget, 'name', ['john', 'lennon'], html=(
|
||||
'<input type="text" class="big" value="john" name="name_0" />'
|
||||
'<input type="text" class="small" value="lennon" name="name_1" />'
|
||||
))
|
||||
self.check_html(widget, 'name', 'john__lennon', html=(
|
||||
'<input type="text" class="big" value="john" name="name_0" />'
|
||||
'<input type="text" class="small" value="lennon" name="name_1" />'
|
||||
))
|
||||
self.check_html(widget, 'name', 'john__lennon', attrs={'id': 'foo'}, html=(
|
||||
'<input id="foo_0" type="text" class="big" value="john" name="name_0" />'
|
||||
'<input id="foo_1" type="text" class="small" value="lennon" name="name_1" />'
|
||||
))
|
||||
|
||||
def test_constructor_attrs(self):
|
||||
widget = MyMultiWidget(
|
||||
widgets=(
|
||||
TextInput(attrs={'class': 'big'}),
|
||||
TextInput(attrs={'class': 'small'}),
|
||||
),
|
||||
attrs={'id': 'bar'},
|
||||
)
|
||||
self.check_html(widget, 'name', ['john', 'lennon'], html=(
|
||||
'<input id="bar_0" type="text" class="big" value="john" name="name_0" />'
|
||||
'<input id="bar_1" type="text" class="small" value="lennon" name="name_1" />'
|
||||
))
|
||||
|
||||
def test_needs_multipart_true(self):
|
||||
"""
|
||||
needs_multipart_form should be True if any widgets need it.
|
||||
"""
|
||||
widget = MyMultiWidget(widgets=(TextInput(), FileInput()))
|
||||
self.assertTrue(widget.needs_multipart_form)
|
||||
|
||||
def test_needs_multipart_false(self):
|
||||
"""
|
||||
needs_multipart_form should be False if no widgets need it.
|
||||
"""
|
||||
widget = MyMultiWidget(widgets=(TextInput(), TextInput()))
|
||||
self.assertFalse(widget.needs_multipart_form)
|
||||
|
||||
def test_nested_multiwidget(self):
|
||||
"""
|
||||
MultiWidgets can be composed of other MultiWidgets.
|
||||
"""
|
||||
widget = ComplexMultiWidget()
|
||||
self.check_html(widget, 'name', 'some text,JP,2007-04-25 06:24:00', html=(
|
||||
"""
|
||||
<input type="text" name="name_0" value="some text" />
|
||||
<select multiple="multiple" name="name_1">
|
||||
<option value="J" selected="selected">John</option>
|
||||
<option value="P" selected="selected">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>
|
||||
<input type="text" name="name_2_0" value="2007-04-25" />
|
||||
<input type="text" name="name_2_1" value="06:24:00" />
|
||||
"""
|
||||
))
|
||||
|
||||
def test_deepcopy(self):
|
||||
"""
|
||||
MultiWidget should define __deepcopy__() (#12048).
|
||||
"""
|
||||
w1 = DeepCopyWidget(choices=[1, 2, 3])
|
||||
w2 = copy.deepcopy(w1)
|
||||
w2.choices = [4, 5, 6]
|
||||
# w2 ought to be independent of w1, since MultiWidget ought
|
||||
# to make a copy of its sub-widgets when it is copied.
|
||||
self.assertEqual(w1.choices, [1, 2, 3])
|
|
@ -0,0 +1,64 @@
|
|||
from django.forms import NullBooleanSelect
|
||||
from django.test import override_settings
|
||||
from django.utils import translation
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class NullBooleanSelectTest(WidgetTest):
|
||||
widget = NullBooleanSelect()
|
||||
|
||||
def test_render_true(self):
|
||||
self.check_html(self.widget, 'is_cool', True, html=(
|
||||
"""<select name="is_cool">
|
||||
<option value="1">Unknown</option>
|
||||
<option value="2" selected="selected">Yes</option>
|
||||
<option value="3">No</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_false(self):
|
||||
self.check_html(self.widget, 'is_cool', False, html=(
|
||||
"""<select name="is_cool">
|
||||
<option value="1">Unknown</option>
|
||||
<option value="2">Yes</option>
|
||||
<option value="3" selected="selected">No</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'is_cool', None, html=(
|
||||
"""<select name="is_cool">
|
||||
<option value="1" selected="selected">Unknown</option>
|
||||
<option value="2">Yes</option>
|
||||
<option value="3">No</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_value(self):
|
||||
self.check_html(self.widget, 'is_cool', '2', html=(
|
||||
"""<select name="is_cool">
|
||||
<option value="1">Unknown</option>
|
||||
<option value="2" selected="selected">Yes</option>
|
||||
<option value="3">No</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
def test_l10n(self):
|
||||
"""
|
||||
Ensure that the NullBooleanSelect widget's options are lazily
|
||||
localized (#17190).
|
||||
"""
|
||||
widget = NullBooleanSelect()
|
||||
|
||||
with translation.override('de-at'):
|
||||
self.check_html(widget, 'id_bool', True, html=(
|
||||
"""
|
||||
<select name="id_bool">
|
||||
<option value="1">Unbekannt</option>
|
||||
<option value="2" selected="selected">Ja</option>
|
||||
<option value="3">Nein</option>
|
||||
</select>
|
||||
"""
|
||||
))
|
|
@ -0,0 +1,26 @@
|
|||
from django.forms import PasswordInput
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class PasswordInputTest(WidgetTest):
|
||||
widget = PasswordInput()
|
||||
|
||||
def test_render(self):
|
||||
self.check_html(self.widget, 'password', '', html='<input type="password" name="password" />')
|
||||
|
||||
def test_render_ignore_value(self):
|
||||
self.check_html(self.widget, 'password', 'secret', html='<input type="password" name="password" />')
|
||||
|
||||
def test_render_value_true(self):
|
||||
"""
|
||||
The render_value argument lets you specify whether the widget should
|
||||
render its value. For security reasons, this is off by default.
|
||||
"""
|
||||
widget = PasswordInput(render_value=True)
|
||||
self.check_html(widget, 'password', '', html='<input type="password" name="password" />')
|
||||
self.check_html(widget, 'password', None, html='<input type="password" name="password" />')
|
||||
self.check_html(
|
||||
widget, 'password', 'test@example.com',
|
||||
html='<input type="password" name="password" value="test@example.com" />',
|
||||
)
|
|
@ -0,0 +1,84 @@
|
|||
from django.forms import RadioSelect
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class RadioSelectTest(WidgetTest):
|
||||
widget = RadioSelect()
|
||||
|
||||
def test_render(self):
|
||||
self.check_html(self.widget, 'beatle', 'J', choices=self.beatles, html=(
|
||||
"""<ul>
|
||||
<li><label><input checked="checked" type="radio" name="beatle" value="J" /> John</label></li>
|
||||
<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
|
||||
<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
|
||||
<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
|
||||
</ul>"""
|
||||
))
|
||||
|
||||
def test_nested_choices(self):
|
||||
nested_choices = (
|
||||
('unknown', 'Unknown'),
|
||||
('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))),
|
||||
('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
|
||||
)
|
||||
html = """
|
||||
<ul id="media">
|
||||
<li>
|
||||
<label for="media_0"><input id="media_0" name="nestchoice" type="radio" value="unknown" /> Unknown</label>
|
||||
</li>
|
||||
<li>Audio<ul id="media_1">
|
||||
<li>
|
||||
<label for="media_1_0"><input id="media_1_0" name="nestchoice" type="radio" value="vinyl" /> Vinyl</label>
|
||||
</li>
|
||||
<li><label for="media_1_1"><input id="media_1_1" name="nestchoice" type="radio" value="cd" /> CD</label></li>
|
||||
</ul></li>
|
||||
<li>Video<ul id="media_2">
|
||||
<li><label for="media_2_0"><input id="media_2_0" name="nestchoice" type="radio" value="vhs" /> VHS</label></li>
|
||||
<li>
|
||||
<label for="media_2_1">
|
||||
<input checked="checked" id="media_2_1" name="nestchoice" type="radio" value="dvd" /> DVD
|
||||
</label>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
"""
|
||||
self.check_html(
|
||||
self.widget, 'nestchoice', 'dvd', choices=nested_choices,
|
||||
attrs={'id': 'media'}, html=html,
|
||||
)
|
||||
|
||||
def test_constructor_attrs(self):
|
||||
"""
|
||||
Attributes provided at instantiation are passed to the constituent
|
||||
inputs.
|
||||
"""
|
||||
widget = RadioSelect(attrs={'id': 'foo'})
|
||||
html = """
|
||||
<ul id="foo">
|
||||
<li>
|
||||
<label for="foo_0"><input checked="checked" type="radio" id="foo_0" value="J" name="beatle" /> John</label>
|
||||
</li>
|
||||
<li><label for="foo_1"><input type="radio" id="foo_1" value="P" name="beatle" /> Paul</label></li>
|
||||
<li><label for="foo_2"><input type="radio" id="foo_2" value="G" name="beatle" /> George</label></li>
|
||||
<li><label for="foo_3"><input type="radio" id="foo_3" value="R" name="beatle" /> Ringo</label></li>
|
||||
</ul>
|
||||
"""
|
||||
self.check_html(widget, 'beatle', 'J', choices=self.beatles, html=html)
|
||||
|
||||
def test_render_attrs(self):
|
||||
"""
|
||||
Attributes provided at render-time are passed to the constituent
|
||||
inputs.
|
||||
"""
|
||||
html = """
|
||||
<ul id="bar">
|
||||
<li>
|
||||
<label for="bar_0"><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label>
|
||||
</li>
|
||||
<li><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li>
|
||||
<li><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li>
|
||||
<li><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></li>
|
||||
</ul>
|
||||
"""
|
||||
self.check_html(self.widget, 'beatle', 'J', choices=self.beatles, attrs={'id': 'bar'}, html=html)
|
|
@ -0,0 +1,250 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
|
||||
from django.forms import Select
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class SelectTest(WidgetTest):
|
||||
widget = Select()
|
||||
nested_widget = Select(choices=(
|
||||
('outer1', 'Outer 1'),
|
||||
('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))),
|
||||
))
|
||||
|
||||
def test_render(self):
|
||||
self.check_html(self.widget, 'beatle', 'J', choices=self.beatles, html=(
|
||||
"""<select name="beatle">
|
||||
<option value="J" selected="selected">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_none(self):
|
||||
"""
|
||||
If the value is None, none of the options are selected.
|
||||
"""
|
||||
self.check_html(self.widget, 'beatle', None, choices=self.beatles, html=(
|
||||
"""<select name="beatle">
|
||||
<option value="J">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_label_value(self):
|
||||
"""
|
||||
If the value corresponds to a label (but not to an option value), none
|
||||
of the options are selected.
|
||||
"""
|
||||
self.check_html(self.widget, 'beatle', 'John', choices=self.beatles, html=(
|
||||
"""<select name="beatle">
|
||||
<option value="J">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_selected(self):
|
||||
"""
|
||||
Only one option can be selected (#8103).
|
||||
"""
|
||||
choices = [('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra')]
|
||||
|
||||
self.check_html(self.widget, 'choices', '0', choices=choices, html=(
|
||||
"""<select name="choices">
|
||||
<option value="0" selected="selected">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="0">extra</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_constructor_attrs(self):
|
||||
"""
|
||||
Select options shouldn't inherit the parent widget attrs.
|
||||
"""
|
||||
widget = Select(
|
||||
attrs={'class': 'super', 'id': 'super'},
|
||||
choices=[(1, 1), (2, 2), (3, 3)],
|
||||
)
|
||||
self.check_html(widget, 'num', 2, html=(
|
||||
"""<select name="num" class="super" id="super">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_compare_to_str(self):
|
||||
"""
|
||||
The value is compared to its str().
|
||||
"""
|
||||
self.check_html(
|
||||
self.widget, 'num', 2,
|
||||
choices=[('1', '1'), ('2', '2'), ('3', '3')],
|
||||
html=(
|
||||
"""<select name="num">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
),
|
||||
)
|
||||
self.check_html(
|
||||
self.widget, 'num', '2',
|
||||
choices=[(1, 1), (2, 2), (3, 3)],
|
||||
html=(
|
||||
"""<select name="num">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
),
|
||||
)
|
||||
self.check_html(
|
||||
self.widget, 'num', 2,
|
||||
choices=[(1, 1), (2, 2), (3, 3)],
|
||||
html=(
|
||||
"""<select name="num">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
),
|
||||
)
|
||||
|
||||
def test_choices_constuctor(self):
|
||||
widget = Select(choices=[(1, 1), (2, 2), (3, 3)])
|
||||
self.check_html(widget, 'num', 2, html=(
|
||||
"""<select name="num">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_choices_constructor_generator(self):
|
||||
"""
|
||||
If choices is passed to the constructor and is a generator, it can be
|
||||
iterated over multiple times without getting consumed.
|
||||
"""
|
||||
def get_choices():
|
||||
for i in range(5):
|
||||
yield (i, i)
|
||||
|
||||
widget = Select(choices=get_choices())
|
||||
self.check_html(widget, 'num', 2, html=(
|
||||
"""<select name="num">
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
</select>"""
|
||||
))
|
||||
self.check_html(widget, 'num', 3, html=(
|
||||
"""<select name="num">
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3" selected="selected">3</option>
|
||||
<option value="4">4</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_choices_constuctor_and_render(self):
|
||||
"""
|
||||
If 'choices' is passed to both the constructor and render(), then
|
||||
they'll both be in the output.
|
||||
"""
|
||||
widget = Select(choices=[(1, 1), (2, 2), (3, 3)])
|
||||
self.check_html(widget, 'num', 2, choices=[(4, 4), (5, 5)], html=(
|
||||
"""<select name="num">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_choices_escaping(self):
|
||||
choices = (('bad', 'you & me'), ('good', mark_safe('you > me')))
|
||||
self.check_html(self.widget, 'escape', None, choices=choices, html=(
|
||||
"""<select name="escape">
|
||||
<option value="bad">you & me</option>
|
||||
<option value="good">you > me</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_choices_unicode(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', 'ŠĐĆŽćžšđ',
|
||||
choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')],
|
||||
html=(
|
||||
"""<select name="email">
|
||||
<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">
|
||||
\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111
|
||||
</option>
|
||||
<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>
|
||||
</select>"""
|
||||
),
|
||||
)
|
||||
|
||||
def test_choices_optgroup(self):
|
||||
"""
|
||||
Choices can be nested one level in order to create HTML optgroups.
|
||||
"""
|
||||
self.check_html(self.nested_widget, 'nestchoice', None, html=(
|
||||
"""<select name="nestchoice">
|
||||
<option value="outer1">Outer 1</option>
|
||||
<optgroup label="Group "1"">
|
||||
<option value="inner1">Inner 1</option>
|
||||
<option value="inner2">Inner 2</option>
|
||||
</optgroup>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_choices_select_outer(self):
|
||||
self.check_html(self.nested_widget, 'nestchoice', 'outer1', html=(
|
||||
"""<select name="nestchoice">
|
||||
<option value="outer1" selected="selected">Outer 1</option>
|
||||
<optgroup label="Group "1"">
|
||||
<option value="inner1">Inner 1</option>
|
||||
<option value="inner2">Inner 2</option>
|
||||
</optgroup>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_choices_select_inner(self):
|
||||
self.check_html(self.nested_widget, 'nestchoice', 'inner1', html=(
|
||||
"""<select name="nestchoice">
|
||||
<option value="outer1">Outer 1</option>
|
||||
<optgroup label="Group "1"">
|
||||
<option value="inner1" selected="selected">Inner 1</option>
|
||||
<option value="inner2">Inner 2</option>
|
||||
</optgroup>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_deepcopy(self):
|
||||
"""
|
||||
__deepcopy__() should copy all attributes properly (#25085).
|
||||
"""
|
||||
widget = Select()
|
||||
obj = copy.deepcopy(widget)
|
||||
self.assertIsNot(widget, obj)
|
||||
self.assertEqual(widget.choices, obj.choices)
|
||||
self.assertIsNot(widget.choices, obj.choices)
|
||||
self.assertEqual(widget.attrs, obj.attrs)
|
||||
self.assertIsNot(widget.attrs, obj.attrs)
|
|
@ -0,0 +1,479 @@
|
|||
from datetime import date
|
||||
|
||||
from django.forms import DateField, Form, SelectDateWidget
|
||||
from django.test import override_settings
|
||||
from django.utils import translation
|
||||
from django.utils.dates import MONTHS_AP
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class SelectDateWidgetTest(WidgetTest):
|
||||
maxDiff = None
|
||||
widget = SelectDateWidget(
|
||||
years=('2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016'),
|
||||
)
|
||||
|
||||
def test_render_empty(self):
|
||||
self.check_html(self.widget, 'mydate', '', html=(
|
||||
"""
|
||||
<select name="mydate_month" id="id_mydate_month">
|
||||
<option value="0">---</option>
|
||||
<option value="1">January</option>
|
||||
<option value="2">February</option>
|
||||
<option value="3">March</option>
|
||||
<option value="4">April</option>
|
||||
<option value="5">May</option>
|
||||
<option value="6">June</option>
|
||||
<option value="7">July</option>
|
||||
<option value="8">August</option>
|
||||
<option value="9">September</option>
|
||||
<option value="10">October</option>
|
||||
<option value="11">November</option>
|
||||
<option value="12">December</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_day" id="id_mydate_day">
|
||||
<option value="0">---</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="18">18</option>
|
||||
<option value="19">19</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="28">28</option>
|
||||
<option value="29">29</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_year" id="id_mydate_year">
|
||||
<option value="0">---</option>
|
||||
<option value="2007">2007</option>
|
||||
<option value="2008">2008</option>
|
||||
<option value="2009">2009</option>
|
||||
<option value="2010">2010</option>
|
||||
<option value="2011">2011</option>
|
||||
<option value="2012">2012</option>
|
||||
<option value="2013">2013</option>
|
||||
<option value="2014">2014</option>
|
||||
<option value="2015">2015</option>
|
||||
<option value="2016">2016</option>
|
||||
</select>
|
||||
"""
|
||||
))
|
||||
|
||||
def test_render_none(self):
|
||||
"""
|
||||
Rendering the None or '' values should yield the same output.
|
||||
"""
|
||||
self.assertHTMLEqual(
|
||||
self.widget.render('mydate', None),
|
||||
self.widget.render('mydate', ''),
|
||||
)
|
||||
|
||||
def test_render_string(self):
|
||||
self.check_html(self.widget, 'mydate', '2010-04-15', html=(
|
||||
"""
|
||||
<select name="mydate_month" id="id_mydate_month">
|
||||
<option value="0">---</option>
|
||||
<option value="1">January</option>
|
||||
<option value="2">February</option>
|
||||
<option value="3">March</option>
|
||||
<option value="4" selected="selected">April</option>
|
||||
<option value="5">May</option>
|
||||
<option value="6">June</option>
|
||||
<option value="7">July</option>
|
||||
<option value="8">August</option>
|
||||
<option value="9">September</option>
|
||||
<option value="10">October</option>
|
||||
<option value="11">November</option>
|
||||
<option value="12">December</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_day" id="id_mydate_day">
|
||||
<option value="0">---</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15" selected="selected">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="18">18</option>
|
||||
<option value="19">19</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="28">28</option>
|
||||
<option value="29">29</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_year" id="id_mydate_year">
|
||||
<option value="0">---</option>
|
||||
<option value="2007">2007</option>
|
||||
<option value="2008">2008</option>
|
||||
<option value="2009">2009</option>
|
||||
<option value="2010" selected="selected">2010</option>
|
||||
<option value="2011">2011</option>
|
||||
<option value="2012">2012</option>
|
||||
<option value="2013">2013</option>
|
||||
<option value="2014">2014</option>
|
||||
<option value="2015">2015</option>
|
||||
<option value="2016">2016</option>
|
||||
</select>
|
||||
"""
|
||||
))
|
||||
|
||||
def test_render_datetime(self):
|
||||
self.assertHTMLEqual(
|
||||
self.widget.render('mydate', date(2010, 4, 15)),
|
||||
self.widget.render('mydate', '2010-04-15'),
|
||||
)
|
||||
|
||||
def test_render_invalid_date(self):
|
||||
"""
|
||||
Invalid dates should still render the failed date.
|
||||
"""
|
||||
self.check_html(self.widget, 'mydate', '2010-02-31', html=(
|
||||
"""
|
||||
<select name="mydate_month" id="id_mydate_month">
|
||||
<option value="0">---</option>
|
||||
<option value="1">January</option>
|
||||
<option value="2" selected="selected">February</option>
|
||||
<option value="3">March</option>
|
||||
<option value="4">April</option>
|
||||
<option value="5">May</option>
|
||||
<option value="6">June</option>
|
||||
<option value="7">July</option>
|
||||
<option value="8">August</option>
|
||||
<option value="9">September</option>
|
||||
<option value="10">October</option>
|
||||
<option value="11">November</option>
|
||||
<option value="12">December</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_day" id="id_mydate_day">
|
||||
<option value="0">---</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="18">18</option>
|
||||
<option value="19">19</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="28">28</option>
|
||||
<option value="29">29</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31" selected="selected">31</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_year" id="id_mydate_year">
|
||||
<option value="0">---</option>
|
||||
<option value="2007">2007</option>
|
||||
<option value="2008">2008</option>
|
||||
<option value="2009">2009</option>
|
||||
<option value="2010" selected="selected">2010</option>
|
||||
<option value="2011">2011</option>
|
||||
<option value="2012">2012</option>
|
||||
<option value="2013">2013</option>
|
||||
<option value="2014">2014</option>
|
||||
<option value="2015">2015</option>
|
||||
<option value="2016">2016</option>
|
||||
</select>
|
||||
"""
|
||||
))
|
||||
|
||||
def test_custom_months(self):
|
||||
widget = SelectDateWidget(months=MONTHS_AP, years=('2013',))
|
||||
self.check_html(widget, 'mydate', '', html=(
|
||||
"""
|
||||
<select name="mydate_month" id="id_mydate_month">
|
||||
<option value="0">---</option>
|
||||
<option value="1">Jan.</option>
|
||||
<option value="2">Feb.</option>
|
||||
<option value="3">March</option>
|
||||
<option value="4">April</option>
|
||||
<option value="5">May</option>
|
||||
<option value="6">June</option>
|
||||
<option value="7">July</option>
|
||||
<option value="8">Aug.</option>
|
||||
<option value="9">Sept.</option>
|
||||
<option value="10">Oct.</option>
|
||||
<option value="11">Nov.</option>
|
||||
<option value="12">Dec.</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_day" id="id_mydate_day">
|
||||
<option value="0">---</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="18">18</option>
|
||||
<option value="19">19</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="28">28</option>
|
||||
<option value="29">29</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_year" id="id_mydate_year">
|
||||
<option value="0">---</option>
|
||||
<option value="2013">2013</option>
|
||||
</select>
|
||||
"""
|
||||
))
|
||||
|
||||
def test_selectdate_required(self):
|
||||
class GetNotRequiredDate(Form):
|
||||
mydate = DateField(widget=SelectDateWidget, required=False)
|
||||
|
||||
class GetRequiredDate(Form):
|
||||
mydate = DateField(widget=SelectDateWidget, required=True)
|
||||
|
||||
self.assertFalse(GetNotRequiredDate().fields['mydate'].widget.is_required)
|
||||
self.assertTrue(GetRequiredDate().fields['mydate'].widget.is_required)
|
||||
|
||||
def test_selectdate_empty_label(self):
|
||||
w = SelectDateWidget(years=('2014',), empty_label='empty_label')
|
||||
|
||||
# Rendering the default state with empty_label setted as string.
|
||||
self.assertInHTML('<option value="0">empty_label</option>', w.render('mydate', ''), count=3)
|
||||
|
||||
w = SelectDateWidget(years=('2014',), empty_label=('empty_year', 'empty_month', 'empty_day'))
|
||||
|
||||
# Rendering the default state with empty_label tuple.
|
||||
self.assertHTMLEqual(
|
||||
w.render('mydate', ''),
|
||||
"""
|
||||
<select name="mydate_month" id="id_mydate_month">
|
||||
<option value="0">empty_month</option>
|
||||
<option value="1">January</option>
|
||||
<option value="2">February</option>
|
||||
<option value="3">March</option>
|
||||
<option value="4">April</option>
|
||||
<option value="5">May</option>
|
||||
<option value="6">June</option>
|
||||
<option value="7">July</option>
|
||||
<option value="8">August</option>
|
||||
<option value="9">September</option>
|
||||
<option value="10">October</option>
|
||||
<option value="11">November</option>
|
||||
<option value="12">December</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_day" id="id_mydate_day">
|
||||
<option value="0">empty_day</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="18">18</option>
|
||||
<option value="19">19</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="28">28</option>
|
||||
<option value="29">29</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
</select>
|
||||
|
||||
<select name="mydate_year" id="id_mydate_year">
|
||||
<option value="0">empty_year</option>
|
||||
<option value="2014">2014</option>
|
||||
</select>
|
||||
""",
|
||||
)
|
||||
|
||||
self.assertRaisesMessage(ValueError, 'empty_label list/tuple must have 3 elements.',
|
||||
SelectDateWidget, years=('2014',), empty_label=('not enough', 'values'))
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('nl')
|
||||
def test_l10n(self):
|
||||
w = SelectDateWidget(
|
||||
years=('2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016')
|
||||
)
|
||||
self.assertEqual(
|
||||
w.value_from_datadict({'date_year': '2010', 'date_month': '8', 'date_day': '13'}, {}, 'date'),
|
||||
'13-08-2010',
|
||||
)
|
||||
|
||||
self.assertHTMLEqual(
|
||||
w.render('date', '13-08-2010'),
|
||||
"""
|
||||
<select name="date_day" id="id_date_day">
|
||||
<option value="0">---</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13" selected="selected">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="18">18</option>
|
||||
<option value="19">19</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="28">28</option>
|
||||
<option value="29">29</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
</select>
|
||||
|
||||
<select name="date_month" id="id_date_month">
|
||||
<option value="0">---</option>
|
||||
<option value="1">januari</option>
|
||||
<option value="2">februari</option>
|
||||
<option value="3">maart</option>
|
||||
<option value="4">april</option>
|
||||
<option value="5">mei</option>
|
||||
<option value="6">juni</option>
|
||||
<option value="7">juli</option>
|
||||
<option value="8" selected="selected">augustus</option>
|
||||
<option value="9">september</option>
|
||||
<option value="10">oktober</option>
|
||||
<option value="11">november</option>
|
||||
<option value="12">december</option>
|
||||
</select>
|
||||
|
||||
<select name="date_year" id="id_date_year">
|
||||
<option value="0">---</option>
|
||||
<option value="2007">2007</option>
|
||||
<option value="2008">2008</option>
|
||||
<option value="2009">2009</option>
|
||||
<option value="2010" selected="selected">2010</option>
|
||||
<option value="2011">2011</option>
|
||||
<option value="2012">2012</option>
|
||||
<option value="2013">2013</option>
|
||||
<option value="2014">2014</option>
|
||||
<option value="2015">2015</option>
|
||||
<option value="2016">2016</option>
|
||||
</select>
|
||||
""",
|
||||
)
|
||||
|
||||
# Even with an invalid date, the widget should reflect the entered value (#17401).
|
||||
self.assertEqual(w.render('mydate', '2010-02-30').count('selected="selected"'), 3)
|
||||
|
||||
# Years before 1900 should work.
|
||||
w = SelectDateWidget(years=('1899',))
|
||||
self.assertEqual(
|
||||
w.value_from_datadict({'date_year': '1899', 'date_month': '8', 'date_day': '13'}, {}, 'date'),
|
||||
'13-08-1899',
|
||||
)
|
|
@ -0,0 +1,125 @@
|
|||
from django.forms import SelectMultiple
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class SelectMultipleTest(WidgetTest):
|
||||
widget = SelectMultiple()
|
||||
numeric_choices = (('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra'))
|
||||
|
||||
def test_render_selected(self):
|
||||
self.check_html(self.widget, 'beatles', ['J'], choices=self.beatles, html=(
|
||||
"""<select multiple="multiple" name="beatles">
|
||||
<option value="J" selected="selected">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_multiple_selected(self):
|
||||
self.check_html(self.widget, 'beatles', ['J', 'P'], choices=self.beatles, html=(
|
||||
"""<select multiple="multiple" name="beatles">
|
||||
<option value="J" selected="selected">John</option>
|
||||
<option value="P" selected="selected">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_none(self):
|
||||
"""
|
||||
If the value is None, none of the options are selected.
|
||||
"""
|
||||
self.check_html(self.widget, 'beatles', None, choices=self.beatles, html=(
|
||||
"""<select multiple="multiple" name="beatles">
|
||||
<option value="J">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_render_value_label(self):
|
||||
"""
|
||||
If the value corresponds to a label (but not to an option value), none
|
||||
of the options are selected.
|
||||
"""
|
||||
self.check_html(self.widget, 'beatles', ['John'], choices=self.beatles, html=(
|
||||
"""<select multiple="multiple" name="beatles">
|
||||
<option value="J">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_multiple_options_same_value(self):
|
||||
"""
|
||||
Multiple options with the same value can be selected (#8103).
|
||||
"""
|
||||
self.check_html(self.widget, 'choices', ['0'], choices=self.numeric_choices, html=(
|
||||
"""<select multiple="multiple" name="choices">
|
||||
<option value="0" selected="selected">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="0" selected="selected">extra</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_multiple_values_invalid(self):
|
||||
"""
|
||||
If multiple values are given, but some of them are not valid, the valid
|
||||
ones are selected.
|
||||
"""
|
||||
self.check_html(self.widget, 'beatles', ['J', 'G', 'foo'], choices=self.beatles, html=(
|
||||
"""<select multiple="multiple" name="beatles">
|
||||
<option value="J" selected="selected">John</option>
|
||||
<option value="P">Paul</option>
|
||||
<option value="G" selected="selected">George</option>
|
||||
<option value="R">Ringo</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_compare_string(self):
|
||||
choices = [('1', '1'), ('2', '2'), ('3', '3')]
|
||||
|
||||
self.check_html(self.widget, 'nums', [2], choices=choices, html=(
|
||||
"""<select multiple="multiple" name="nums">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
self.check_html(self.widget, 'nums', ['2'], choices=choices, html=(
|
||||
"""<select multiple="multiple" name="nums">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
self.check_html(self.widget, 'nums', [2], choices=choices, html=(
|
||||
"""<select multiple="multiple" name="nums">
|
||||
<option value="1">1</option>
|
||||
<option value="2" selected="selected">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>"""
|
||||
))
|
||||
|
||||
def test_optgroup_select_multiple(self):
|
||||
widget = SelectMultiple(choices=(
|
||||
('outer1', 'Outer 1'),
|
||||
('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))),
|
||||
))
|
||||
self.check_html(widget, 'nestchoice', ['outer1', 'inner2'], html=(
|
||||
"""<select multiple="multiple" name="nestchoice">
|
||||
<option value="outer1" selected="selected">Outer 1</option>
|
||||
<optgroup label="Group "1"">
|
||||
<option value="inner1">Inner 1</option>
|
||||
<option value="inner2" selected="selected">Inner 2</option>
|
||||
</optgroup>
|
||||
</select>"""
|
||||
))
|
|
@ -0,0 +1,51 @@
|
|||
from datetime import date, datetime, time
|
||||
|
||||
from django.forms import SplitDateTimeWidget
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class SplitDateTimeWidgetTest(WidgetTest):
|
||||
widget = SplitDateTimeWidget()
|
||||
|
||||
def test_render_empty(self):
|
||||
self.check_html(self.widget, 'date', '', html=(
|
||||
'<input type="text" name="date_0" /><input type="text" name="date_1" />'
|
||||
))
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'date', None, html=(
|
||||
'<input type="text" name="date_0" /><input type="text" name="date_1" />'
|
||||
))
|
||||
|
||||
def test_render_datetime(self):
|
||||
self.check_html(self.widget, 'date', datetime(2006, 1, 10, 7, 30), html=(
|
||||
'<input type="text" name="date_0" value="2006-01-10" />'
|
||||
'<input type="text" name="date_1" value="07:30:00" />'
|
||||
))
|
||||
|
||||
def test_render_date_and_time(self):
|
||||
self.check_html(self.widget, 'date', [date(2006, 1, 10), time(7, 30)], html=(
|
||||
'<input type="text" name="date_0" value="2006-01-10" />'
|
||||
'<input type="text" name="date_1" value="07:30:00" />'
|
||||
))
|
||||
|
||||
def test_constructor_attrs(self):
|
||||
widget = SplitDateTimeWidget(attrs={'class': 'pretty'})
|
||||
self.check_html(widget, 'date', datetime(2006, 1, 10, 7, 30), html=(
|
||||
'<input type="text" class="pretty" value="2006-01-10" name="date_0" />'
|
||||
'<input type="text" class="pretty" value="07:30:00" name="date_1" />'
|
||||
))
|
||||
|
||||
def test_formatting(self):
|
||||
"""
|
||||
Use 'date_format' and 'time_format' to change the way a value is
|
||||
displayed.
|
||||
"""
|
||||
widget = SplitDateTimeWidget(
|
||||
date_format='%d/%m/%Y', time_format='%H:%M',
|
||||
)
|
||||
self.check_html(widget, 'date', datetime(2006, 1, 10, 7, 30), html=(
|
||||
'<input type="text" name="date_0" value="10/01/2006" />'
|
||||
'<input type="text" name="date_1" value="07:30" />'
|
||||
))
|
|
@ -0,0 +1,42 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.forms import SplitHiddenDateTimeWidget
|
||||
from django.test import override_settings
|
||||
from django.utils import translation
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class SplitHiddenDateTimeWidgetTest(WidgetTest):
|
||||
widget = SplitHiddenDateTimeWidget()
|
||||
|
||||
def test_render_empty(self):
|
||||
self.check_html(self.widget, 'date', '', html=(
|
||||
'<input type="hidden" name="date_0" /><input type="hidden" name="date_1" />'
|
||||
))
|
||||
|
||||
def test_render_value(self):
|
||||
d = datetime(2007, 9, 17, 12, 51, 34, 482548)
|
||||
self.check_html(self.widget, 'date', d, html=(
|
||||
'<input type="hidden" name="date_0" value="2007-09-17" />'
|
||||
'<input type="hidden" name="date_1" value="12:51:34" />'
|
||||
))
|
||||
self.check_html(self.widget, 'date', datetime(2007, 9, 17, 12, 51, 34), html=(
|
||||
'<input type="hidden" name="date_0" value="2007-09-17" />'
|
||||
'<input type="hidden" name="date_1" value="12:51:34" />'
|
||||
))
|
||||
self.check_html(self.widget, 'date', datetime(2007, 9, 17, 12, 51), html=(
|
||||
'<input type="hidden" name="date_0" value="2007-09-17" />'
|
||||
'<input type="hidden" name="date_1" value="12:51:00" />'
|
||||
))
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('de-at')
|
||||
def test_l10n(self):
|
||||
d = datetime(2007, 9, 17, 12, 51)
|
||||
self.check_html(self.widget, 'date', d, html=(
|
||||
"""
|
||||
<input type="hidden" name="date_0" value="17.09.2007" />
|
||||
<input type="hidden" name="date_1" value="12:51:00" />
|
||||
"""
|
||||
))
|
|
@ -0,0 +1,34 @@
|
|||
from django.forms import Textarea
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class TextareaTest(WidgetTest):
|
||||
widget = Textarea()
|
||||
|
||||
def test_render(self):
|
||||
self.check_html(self.widget, 'msg', 'value', html=(
|
||||
'<textarea rows="10" cols="40" name="msg">value</textarea>'
|
||||
))
|
||||
|
||||
def test_render_required(self):
|
||||
widget = Textarea()
|
||||
widget.is_required = True
|
||||
self.check_html(widget, 'msg', 'value', html='<textarea rows="10" cols="40" name="msg">value</textarea>')
|
||||
|
||||
def test_render_empty(self):
|
||||
self.check_html(self.widget, 'msg', '', html='<textarea rows="10" cols="40" name="msg"></textarea>')
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'msg', None, html='<textarea rows="10" cols="40" name="msg"></textarea>')
|
||||
|
||||
def test_escaping(self):
|
||||
self.check_html(self.widget, 'msg', 'some "quoted" & ampersanded value', html=(
|
||||
'<textarea rows="10" cols="40" name="msg">some "quoted" & ampersanded value</textarea>'
|
||||
))
|
||||
|
||||
def test_mark_safe(self):
|
||||
self.check_html(self.widget, 'msg', mark_safe('pre "quoted" value'), html=(
|
||||
'<textarea rows="10" cols="40" name="msg">pre "quoted" value</textarea>'
|
||||
))
|
|
@ -0,0 +1,78 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.forms import TextInput
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class TextInputTest(WidgetTest):
|
||||
widget = TextInput()
|
||||
|
||||
def test_render(self):
|
||||
self.check_html(self.widget, 'email', '', html='<input type="text" name="email" />')
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'email', None, html='<input type="text" name="email" />')
|
||||
|
||||
def test_render_value(self):
|
||||
self.check_html(self.widget, 'email', 'test@example.com', html=(
|
||||
'<input type="text" name="email" value="test@example.com" />'
|
||||
))
|
||||
|
||||
def test_render_boolean(self):
|
||||
"""
|
||||
Boolean values are rendered to their string forms ("True" and
|
||||
"False").
|
||||
"""
|
||||
self.check_html(self.widget, 'get_spam', False, html=(
|
||||
'<input type="text" name="get_spam" value="False" />'
|
||||
))
|
||||
self.check_html(self.widget, 'get_spam', True, html=(
|
||||
'<input type="text" name="get_spam" value="True" />'
|
||||
))
|
||||
|
||||
def test_render_quoted(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', 'some "quoted" & ampersanded value',
|
||||
html='<input type="text" name="email" value="some "quoted" & ampersanded value" />',
|
||||
)
|
||||
|
||||
def test_render_custom_attrs(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', 'test@example.com', attrs={'class': 'fun'},
|
||||
html='<input type="text" name="email" value="test@example.com" class="fun" />',
|
||||
)
|
||||
|
||||
def test_render_unicode(self):
|
||||
self.check_html(
|
||||
self.widget, 'email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'},
|
||||
html=(
|
||||
'<input type="text" name="email" '
|
||||
'value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />'
|
||||
),
|
||||
)
|
||||
|
||||
def test_constructor_attrs(self):
|
||||
widget = TextInput(attrs={'class': 'fun', 'type': 'email'})
|
||||
self.check_html(widget, 'email', '', html='<input type="email" class="fun" name="email" />')
|
||||
self.check_html(
|
||||
widget, 'email', 'foo@example.com',
|
||||
html='<input type="email" class="fun" value="foo@example.com" name="email" />',
|
||||
)
|
||||
|
||||
def test_attrs_precedence(self):
|
||||
"""
|
||||
`attrs` passed to render() get precedence over those passed to the
|
||||
constructor
|
||||
"""
|
||||
widget = TextInput(attrs={'class': 'pretty'})
|
||||
self.check_html(
|
||||
widget, 'email', '', attrs={'class': 'special'},
|
||||
html='<input type="text" class="special" name="email" />',
|
||||
)
|
||||
|
||||
def test_attrs_safestring(self):
|
||||
widget = TextInput(attrs={'onBlur': mark_safe("function('foo')")})
|
||||
self.check_html(widget, 'email', '', html='<input onBlur="function(\'foo\')" type="text" name="email" />')
|
|
@ -0,0 +1,50 @@
|
|||
from datetime import time
|
||||
|
||||
from django.forms import TimeInput
|
||||
from django.test import override_settings
|
||||
from django.utils import translation
|
||||
|
||||
from .base import WidgetTest
|
||||
|
||||
|
||||
class TimeInputTest(WidgetTest):
|
||||
widget = TimeInput()
|
||||
|
||||
def test_render_none(self):
|
||||
self.check_html(self.widget, 'time', None, html='<input type="text" name="time" />')
|
||||
|
||||
def test_render_value(self):
|
||||
"""
|
||||
The microseconds are trimmed on display, by default.
|
||||
"""
|
||||
t = time(12, 51, 34, 482548)
|
||||
self.assertEqual(str(t), '12:51:34.482548')
|
||||
self.check_html(self.widget, 'time', t, html='<input type="text" name="time" value="12:51:34" />')
|
||||
self.check_html(self.widget, 'time', time(12, 51, 34), html=(
|
||||
'<input type="text" name="time" value="12:51:34" />'
|
||||
))
|
||||
self.check_html(self.widget, 'time', time(12, 51), html=(
|
||||
'<input type="text" name="time" value="12:51:00" />'
|
||||
))
|
||||
|
||||
def test_string(self):
|
||||
"""
|
||||
We should be able to initialize from a unicode value.
|
||||
"""
|
||||
self.check_html(self.widget, 'time', '13:12:11', html=(
|
||||
'<input type="text" name="time" value="13:12:11" />'
|
||||
))
|
||||
|
||||
def test_format(self):
|
||||
"""
|
||||
Use 'format' to change the way a value is displayed.
|
||||
"""
|
||||
t = time(12, 51, 34, 482548)
|
||||
widget = TimeInput(format='%H:%M', attrs={'type': 'time'})
|
||||
self.check_html(widget, 'time', t, html='<input type="time" name="time" value="12:51" />')
|
||||
|
||||
@override_settings(USE_L10N=True)
|
||||
@translation.override('de-at')
|
||||
def test_l10n(self):
|
||||
t = time(12, 51, 34, 482548)
|
||||
self.check_html(self.widget, 'time', t, html='<input type="text" name="time" value="12:51:34" />')
|
Loading…
Reference in New Issue