diff options
| author | Preston Timmons <prestontimmons@gmail.com> | 2015-08-30 21:13:42 -0500 |
|---|---|---|
| committer | Preston Timmons <prestontimmons@gmail.com> | 2015-08-31 23:03:55 -0500 |
| commit | 4c30fa905d9d47b3a2ee82095b1fe56cc2ec2ab5 (patch) | |
| tree | 2bb04d4ecef539e4ebefbd6d750dfd240ddd7f0c /tests/forms_tests | |
| parent | 5153a3bfdcec82324d67ff79862384288cf6afe6 (diff) | |
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.
Diffstat (limited to 'tests/forms_tests')
26 files changed, 2270 insertions, 1825 deletions
diff --git a/tests/forms_tests/field_tests/__init__.py b/tests/forms_tests/field_tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/forms_tests/field_tests/__init__.py diff --git a/tests/forms_tests/field_tests/test_datefield.py b/tests/forms_tests/field_tests/test_datefield.py new file mode 100644 index 0000000000..6b66cb2d52 --- /dev/null +++ b/tests/forms_tests/field_tests/test_datefield.py @@ -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()) diff --git a/tests/forms_tests/field_tests/test_multivaluefield.py b/tests/forms_tests/field_tests/test_multivaluefield.py new file mode 100644 index 0000000000..258d67bdcd --- /dev/null +++ b/tests/forms_tests/field_tests/test_multivaluefield.py @@ -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', + ) diff --git a/tests/forms_tests/tests/test_widgets.py b/tests/forms_tests/tests/test_widgets.py index 7c66c4c391..d1010fe174 100644 --- a/tests/forms_tests/tests/test_widgets.py +++ b/tests/forms_tests/tests/test_widgets.py @@ -1,649 +1,23 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -import copy -import datetime - from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase -from django.core.files.uploadedfile import SimpleUploadedFile from django.core.urlresolvers import reverse from django.forms import ( - BooleanField, CharField, CheckboxInput, CheckboxSelectMultiple, - ChoiceField, ClearableFileInput, DateField, DateInput, DateTimeInput, - FileInput, Form, HiddenInput, MultipleChoiceField, MultipleHiddenInput, - MultiValueField, MultiWidget, NullBooleanSelect, PasswordInput, - RadioSelect, Select, SelectDateWidget, SelectMultiple, SplitDateTimeField, - SplitDateTimeWidget, Textarea, TextInput, TimeInput, ValidationError, + CheckboxSelectMultiple, ClearableFileInput, RadioSelect, TextInput, ) from django.forms.widgets import ( ChoiceFieldRenderer, ChoiceInput, RadioFieldRenderer, ) from django.test import SimpleTestCase, override_settings -from django.utils import six, translation -from django.utils.dates import MONTHS_AP +from django.utils import six from django.utils.encoding import force_text, python_2_unicode_compatible -from django.utils.safestring import SafeData, mark_safe +from django.utils.safestring import SafeData from ..models import Article class FormsWidgetTests(SimpleTestCase): - # Each Widget class corresponds to an HTML form widget. A Widget knows how to - # render itself, given a field name and some data. Widgets don't perform - # validation. - def test_textinput(self): - w = TextInput() - self.assertHTMLEqual(w.render('email', ''), '<input type="text" name="email" />') - self.assertHTMLEqual(w.render('email', None), '<input type="text" name="email" />') - self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="text" name="email" value="test@example.com" />') - self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="text" name="email" value="some "quoted" & ampersanded value" />') - self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="text" name="email" value="test@example.com" class="fun" />') - - self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />') - - # You can also pass 'attrs' to the constructor: - w = TextInput(attrs={'class': 'fun', 'type': 'email'}) - self.assertHTMLEqual(w.render('email', ''), '<input type="email" class="fun" name="email" />') - self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="email" class="fun" value="foo@example.com" name="email" />') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = TextInput(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="text" class="special" name="email" />') - - # 'attrs' can be safe-strings if needed) - w = TextInput(attrs={'onBlur': mark_safe("function('foo')")}) - self.assertHTMLEqual(w.render('email', ''), '<input onBlur="function(\'foo\')" type="text" name="email" />') - - def test_passwordinput(self): - w = PasswordInput() - self.assertHTMLEqual(w.render('email', ''), '<input type="password" name="email" />') - self.assertHTMLEqual(w.render('email', None), '<input type="password" name="email" />') - self.assertHTMLEqual(w.render('email', 'secret'), '<input type="password" name="email" />') - - # The render_value argument lets you specify whether the widget should render - # its value. For security reasons, this is off by default. - w = PasswordInput(render_value=True) - self.assertHTMLEqual(w.render('email', ''), '<input type="password" name="email" />') - self.assertHTMLEqual(w.render('email', None), '<input type="password" name="email" />') - self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="password" name="email" value="test@example.com" />') - self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="password" name="email" value="some "quoted" & ampersanded value" />') - self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="password" name="email" value="test@example.com" class="fun" />') - - # You can also pass 'attrs' to the constructor: - w = PasswordInput(attrs={'class': 'fun'}, render_value=True) - self.assertHTMLEqual(w.render('email', ''), '<input type="password" class="fun" name="email" />') - self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="password" class="fun" value="foo@example.com" name="email" />') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = PasswordInput(attrs={'class': 'pretty'}, render_value=True) - self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="password" class="special" name="email" />') - - self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />') - - def test_hiddeninput(self): - w = HiddenInput() - self.assertHTMLEqual(w.render('email', ''), '<input type="hidden" name="email" />') - self.assertHTMLEqual(w.render('email', None), '<input type="hidden" name="email" />') - self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="hidden" name="email" value="test@example.com" />') - self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="hidden" name="email" value="some "quoted" & ampersanded value" />') - self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="hidden" name="email" value="test@example.com" class="fun" />') - - # You can also pass 'attrs' to the constructor: - w = HiddenInput(attrs={'class': 'fun'}) - self.assertHTMLEqual(w.render('email', ''), '<input type="hidden" class="fun" name="email" />') - self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="hidden" class="fun" value="foo@example.com" name="email" />') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = HiddenInput(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="hidden" class="special" name="email" />') - - self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = HiddenInput(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="hidden" class="special" name="email" />') - - # Boolean values are rendered to their string forms ("True" and "False"). - w = HiddenInput() - self.assertHTMLEqual(w.render('get_spam', False), '<input type="hidden" name="get_spam" value="False" />') - self.assertHTMLEqual(w.render('get_spam', True), '<input type="hidden" name="get_spam" value="True" />') - - def test_multiplehiddeninput(self): - w = MultipleHiddenInput() - self.assertHTMLEqual(w.render('email', []), '') - self.assertHTMLEqual(w.render('email', None), '') - self.assertHTMLEqual(w.render('email', ['test@example.com']), '<input type="hidden" name="email" value="test@example.com" />') - self.assertHTMLEqual(w.render('email', ['some "quoted" & ampersanded value']), '<input type="hidden" name="email" value="some "quoted" & ampersanded value" />') - self.assertHTMLEqual(w.render('email', ['test@example.com', 'foo@example.com']), '<input type="hidden" name="email" value="test@example.com" />\n<input type="hidden" name="email" value="foo@example.com" />') - self.assertHTMLEqual(w.render('email', ['test@example.com'], attrs={'class': 'fun'}), '<input type="hidden" name="email" value="test@example.com" class="fun" />') - self.assertHTMLEqual(w.render('email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'}), '<input type="hidden" name="email" value="test@example.com" class="fun" />\n<input type="hidden" name="email" value="foo@example.com" class="fun" />') - - # You can also pass 'attrs' to the constructor: - w = MultipleHiddenInput(attrs={'class': 'fun'}) - self.assertHTMLEqual(w.render('email', []), '') - self.assertHTMLEqual(w.render('email', ['foo@example.com']), '<input type="hidden" class="fun" value="foo@example.com" name="email" />') - self.assertHTMLEqual(w.render('email', ['foo@example.com', 'test@example.com']), '<input type="hidden" class="fun" value="foo@example.com" name="email" />\n<input type="hidden" class="fun" value="test@example.com" name="email" />') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = MultipleHiddenInput(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('email', ['foo@example.com'], attrs={'class': 'special'}), '<input type="hidden" class="special" value="foo@example.com" name="email" />') - - self.assertHTMLEqual(w.render('email', ['ŠĐĆŽćžšđ'], attrs={'class': 'fun'}), '<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = MultipleHiddenInput(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('email', ['foo@example.com'], attrs={'class': 'special'}), '<input type="hidden" class="special" value="foo@example.com" name="email" />') - - # Each input gets a separate ID. - w = MultipleHiddenInput() - self.assertHTMLEqual(w.render('letters', list('abc'), attrs={'id': 'hideme'}), '<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" />') - - def test_fileinput(self): - # FileInput widgets don't ever show the value, because the old value is of no use - # if you are updating the form or if the provided file generated an error. - w = FileInput() - self.assertHTMLEqual(w.render('email', ''), '<input type="file" name="email" />') - self.assertHTMLEqual(w.render('email', None), '<input type="file" name="email" />') - self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="file" name="email" />') - self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="file" name="email" />') - self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="file" name="email" class="fun" />') - - # You can also pass 'attrs' to the constructor: - w = FileInput(attrs={'class': 'fun'}) - self.assertHTMLEqual(w.render('email', ''), '<input type="file" class="fun" name="email" />') - self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="file" class="fun" name="email" />') - - self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="file" class="fun" name="email" />') - - def test_textarea(self): - w = Textarea() - self.assertHTMLEqual(w.render('msg', ''), '<textarea rows="10" cols="40" name="msg"></textarea>') - self.assertHTMLEqual(w.render('msg', None), '<textarea rows="10" cols="40" name="msg"></textarea>') - self.assertHTMLEqual(w.render('msg', 'value'), '<textarea rows="10" cols="40" name="msg">value</textarea>') - self.assertHTMLEqual(w.render('msg', 'some "quoted" & ampersanded value'), '<textarea rows="10" cols="40" name="msg">some "quoted" & ampersanded value</textarea>') - self.assertHTMLEqual(w.render('msg', mark_safe('pre "quoted" value')), '<textarea rows="10" cols="40" name="msg">pre "quoted" value</textarea>') - self.assertHTMLEqual(w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20}), '<textarea class="pretty" rows="20" cols="40" name="msg">value</textarea>') - - # You can also pass 'attrs' to the constructor: - w = Textarea(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('msg', ''), '<textarea rows="10" cols="40" name="msg" class="pretty"></textarea>') - self.assertHTMLEqual(w.render('msg', 'example'), '<textarea rows="10" cols="40" name="msg" class="pretty">example</textarea>') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = Textarea(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('msg', '', attrs={'class': 'special'}), '<textarea rows="10" cols="40" name="msg" class="special"></textarea>') - - self.assertHTMLEqual(w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<textarea rows="10" cols="40" name="msg" class="fun">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>') - - def test_checkboxinput(self): - w = CheckboxInput() - self.assertHTMLEqual(w.render('is_cool', ''), '<input type="checkbox" name="is_cool" />') - self.assertHTMLEqual(w.render('is_cool', None), '<input type="checkbox" name="is_cool" />') - self.assertHTMLEqual(w.render('is_cool', False), '<input type="checkbox" name="is_cool" />') - self.assertHTMLEqual(w.render('is_cool', True), '<input checked="checked" type="checkbox" name="is_cool" />') - - # Using any value that's not in ('', None, False, True) will check the checkbox - # and set the 'value' attribute. - self.assertHTMLEqual(w.render('is_cool', 'foo'), '<input checked="checked" type="checkbox" name="is_cool" value="foo" />') - - self.assertHTMLEqual(w.render('is_cool', False, attrs={'class': 'pretty'}), '<input type="checkbox" name="is_cool" class="pretty" />') - - # regression for #17114 - self.assertHTMLEqual(w.render('is_cool', 0), '<input checked="checked" type="checkbox" name="is_cool" value="0" />') - self.assertHTMLEqual(w.render('is_cool', 1), '<input checked="checked" type="checkbox" name="is_cool" value="1" />') - - # You can also pass 'attrs' to the constructor: - w = CheckboxInput(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('is_cool', ''), '<input type="checkbox" class="pretty" name="is_cool" />') - - # 'attrs' passed to render() get precedence over those passed to the constructor: - w = CheckboxInput(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('is_cool', '', attrs={'class': 'special'}), '<input type="checkbox" class="special" name="is_cool" />') - - # 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. - w = CheckboxInput(check_test=lambda value: value.startswith('hello')) - self.assertHTMLEqual(w.render('greeting', ''), '<input type="checkbox" name="greeting" />') - self.assertHTMLEqual(w.render('greeting', 'hello'), '<input checked="checked" type="checkbox" name="greeting" value="hello" />') - self.assertHTMLEqual(w.render('greeting', 'hello there'), '<input checked="checked" type="checkbox" name="greeting" value="hello there" />') - self.assertHTMLEqual(w.render('greeting', 'hello & goodbye'), '<input checked="checked" type="checkbox" name="greeting" value="hello & goodbye" />') - - # Ticket #17888: calling check_test shouldn't swallow exceptions - with self.assertRaises(AttributeError): - w.render('greeting', True) - - # 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(w.value_from_datadict({}, {}, 'testing')) - - value = w.value_from_datadict({'testing': '0'}, {}, 'testing') - self.assertIsInstance(value, bool) - self.assertTrue(value) - - def test_select(self): - w = Select() - self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # If the value is None, none of the options are selected: - self.assertHTMLEqual(w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select name="beatle"> -<option value="J">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select>""") - - # If the value corresponds to a label (but not to an option value), none of the options are selected: - self.assertHTMLEqual(w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select name="beatle"> -<option value="J">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select>""") - - # Only one option can be selected, see #8103: - self.assertHTMLEqual(w.render('choices', '0', choices=(('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra'))), """<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>""") - - # The value is compared to its str(): - self.assertHTMLEqual(w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - self.assertHTMLEqual(w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]), """<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - self.assertHTMLEqual(w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]), """<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - - # The 'choices' argument can be any iterable: - from itertools import chain - - def get_choices(): - for i in range(5): - yield (i, i) - self.assertHTMLEqual(w.render('num', 2, choices=get_choices()), """<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>""") - things = ({'id': 1, 'name': 'And Boom'}, {'id': 2, 'name': 'One More Thing!'}) - - class SomeForm(Form): - somechoice = ChoiceField(choices=chain((('', '-' * 9),), [(thing['id'], thing['name']) for thing in things])) - f = SomeForm() - self.assertHTMLEqual(f.as_table(), '<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>') - self.assertHTMLEqual(f.as_table(), '<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>') - f = SomeForm({'somechoice': 2}) - self.assertHTMLEqual(f.as_table(), '<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="">---------</option>\n<option value="1">And Boom</option>\n<option value="2" selected="selected">One More Thing!</option>\n</select></td></tr>') - - # You can also pass 'choices' to the constructor: - w = Select(choices=[(1, 1), (2, 2), (3, 3)]) - self.assertHTMLEqual(w.render('num', 2), """<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - - # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: - self.assertHTMLEqual(w.render('num', 2, choices=[(4, 4), (5, 5)]), """<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>""") - - # Choices are escaped correctly - self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<select name="escape"> -<option value="1">1</option> -<option value="2">2</option> -<option value="3">3</option> -<option value="bad">you & me</option> -<option value="good">you > me</option> -</select>""") - - # Unicode choices are correctly rendered as HTML - self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>') - - # If choices is passed to the constructor and is a generator, it can be iterated - # over multiple times without getting consumed: - w = Select(choices=get_choices()) - self.assertHTMLEqual(w.render('num', 2), """<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.assertHTMLEqual(w.render('num', 3), """<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>""") - - # Choices can be nested one level in order to create HTML optgroups: - w.choices = ( - ('outer1', 'Outer 1'), - ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))), - ) - self.assertHTMLEqual(w.render('nestchoice', None), """<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>""") - - self.assertHTMLEqual(w.render('nestchoice', 'outer1'), """<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>""") - - self.assertHTMLEqual(w.render('nestchoice', 'inner1'), """<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_nullbooleanselect(self): - w = NullBooleanSelect() - self.assertTrue(w.render('is_cool', True), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected="selected">Yes</option> -<option value="3">No</option> -</select>""") - self.assertHTMLEqual(w.render('is_cool', False), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected="selected">No</option> -</select>""") - self.assertHTMLEqual(w.render('is_cool', None), """<select name="is_cool"> -<option value="1" selected="selected">Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> -</select>""") - self.assertHTMLEqual(w.render('is_cool', '2'), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected="selected">Yes</option> -<option value="3">No</option> -</select>""") - self.assertHTMLEqual(w.render('is_cool', '3'), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected="selected">No</option> -</select>""") - - def test_selectmultiple(self): - w = SelectMultiple() - self.assertHTMLEqual(w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - self.assertHTMLEqual(w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - self.assertHTMLEqual(w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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" selected="selected">Ringo</option> -</select>""") - - # If the value is None, none of the options are selected: - self.assertHTMLEqual(w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # If the value corresponds to a label (but not to an option value), none of the options are selected: - self.assertHTMLEqual(w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # Multiple options (with the same value) can be selected, see #8103: - self.assertHTMLEqual(w.render('choices', ['0'], choices=(('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra'))), """<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>""") - - # If multiple values are given, but some of them are not valid, the valid ones are selected: - self.assertHTMLEqual(w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # The value is compared to its str(): - self.assertHTMLEqual(w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - self.assertHTMLEqual(w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]), """<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - self.assertHTMLEqual(w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]), """<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - - # The 'choices' argument can be any iterable: - def get_choices(): - for i in range(5): - yield (i, i) - self.assertHTMLEqual(w.render('nums', [2], choices=get_choices()), """<select multiple="multiple" name="nums"> -<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>""") - - # You can also pass 'choices' to the constructor: - w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) - self.assertHTMLEqual(w.render('nums', [2]), """<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select>""") - - # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: - self.assertHTMLEqual(w.render('nums', [2], choices=[(4, 4), (5, 5)]), """<select multiple="multiple" name="nums"> -<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>""") - - # Choices are escaped correctly - self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<select multiple="multiple" name="escape"> -<option value="1">1</option> -<option value="2">2</option> -<option value="3">3</option> -<option value="bad">you & me</option> -<option value="good">you > me</option> -</select>""") - - # Unicode choices are correctly rendered as HTML - self.assertHTMLEqual(w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>') - - # Choices can be nested one level in order to create HTML optgroups: - w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2')))) - self.assertHTMLEqual(w.render('nestchoice', None), """<select multiple="multiple" 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>""") - - self.assertHTMLEqual(w.render('nestchoice', ['outer1']), """<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">Inner 2</option> -</optgroup> -</select>""") - - self.assertHTMLEqual(w.render('nestchoice', ['inner1']), """<select multiple="multiple" 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>""") - - self.assertHTMLEqual(w.render('nestchoice', ['outer1', 'inner2']), """<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>""") - - def test_radioselect(self): - w = RadioSelect() - self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # If the value is None, none of the options are checked: - self.assertHTMLEqual(w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul> -<li><label><input 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>""") - - # If the value corresponds to a label (but not to an option value), none of the options are checked: - self.assertHTMLEqual(w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul> -<li><label><input 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>""") - - # The value is compared to its str(): - self.assertHTMLEqual(w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul>""") - self.assertHTMLEqual(w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]), """<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul>""") - self.assertHTMLEqual(w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]), """<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul>""") - - # The 'choices' argument can be any iterable: - def get_choices(): - for i in range(5): - yield (i, i) - self.assertHTMLEqual(w.render('num', 2, choices=get_choices()), """<ul> -<li><label><input type="radio" name="num" value="0" /> 0</label></li> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -<li><label><input type="radio" name="num" value="4" /> 4</label></li> -</ul>""") - - # You can also pass 'choices' to the constructor: - w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)]) - self.assertHTMLEqual(w.render('num', 2), """<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul>""") - - # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: - self.assertHTMLEqual(w.render('num', 2, choices=[(4, 4), (5, 5)]), """<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -<li><label><input type="radio" name="num" value="4" /> 4</label></li> -<li><label><input type="radio" name="num" value="5" /> 5</label></li> -</ul>""") - - # Choices are escaped correctly - w = RadioSelect() - self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<ul> -<li><label><input type="radio" name="escape" value="bad" /> you & me</label></li> -<li><label><input type="radio" name="escape" value="good" /> you > me</label></li> -</ul>""") - - # Unicode choices are correctly rendered as HTML - w = RadioSelect() - self.assertHTMLEqual(six.text_type(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])), '<ul>\n<li><label><input checked="checked" type="radio" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="radio" name="email" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>') - - # Attributes provided at instantiation are passed to the constituent inputs - w = RadioSelect(attrs={'id': 'foo'}) - self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # Attributes provided at render-time are passed to the constituent inputs - w = RadioSelect() - self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id': 'bar'}), """<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>""") def test_radiofieldrenderer(self): # RadioSelect uses a RadioFieldRenderer to render the individual radio inputs. @@ -732,181 +106,6 @@ beatle J R Ringo False""") <p><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></p> </div>""") - def test_nested_choices(self): - # Choices can be nested for radio buttons: - w = RadioSelect() - w.choices = ( - ('unknown', 'Unknown'), - ('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))), - ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))), - ) - self.assertHTMLEqual(w.render('nestchoice', 'dvd', attrs={'id': 'media'}), """<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>""") - - # Choices can be nested for checkboxes: - w = CheckboxSelectMultiple() - w.choices = ( - ('unknown', 'Unknown'), - ('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))), - ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))), - ) - self.assertHTMLEqual(w.render('nestchoice', ('vinyl', 'dvd'), attrs={'id': 'media'}), """<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>""") - - def test_checkboxselectmultiple(self): - w = CheckboxSelectMultiple() - self.assertHTMLEqual(w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - self.assertHTMLEqual(w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - self.assertHTMLEqual(w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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 checked="checked" type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul>""") - - # If the value is None, none of the options are selected: - self.assertHTMLEqual(w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # If the value corresponds to a label (but not to an option value), none of the options are selected: - self.assertHTMLEqual(w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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>""") - - # If multiple values are given, but some of them are not valid, the valid ones are selected: - self.assertHTMLEqual(w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<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 checked="checked" type="checkbox" name="beatles" value="G" /> George</label></li> -<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul>""") - - # The value is compared to its str(): - self.assertHTMLEqual(w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul>""") - self.assertHTMLEqual(w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]), """<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul>""") - self.assertHTMLEqual(w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]), """<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul>""") - - # The 'choices' argument can be any iterable: - def get_choices(): - for i in range(5): - yield (i, i) - self.assertHTMLEqual(w.render('nums', [2], choices=get_choices()), """<ul> -<li><label><input type="checkbox" name="nums" value="0" /> 0</label></li> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> -</ul>""") - - # You can also pass 'choices' to the constructor: - w = CheckboxSelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) - self.assertHTMLEqual(w.render('nums', [2]), """<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul>""") - - # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: - self.assertHTMLEqual(w.render('nums', [2], choices=[(4, 4), (5, 5)]), """<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> -<li><label><input type="checkbox" name="nums" value="5" /> 5</label></li> -</ul>""") - - # Choices are escaped correctly - self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<ul> -<li><label><input type="checkbox" name="escape" value="1" /> 1</label></li> -<li><label><input type="checkbox" name="escape" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="escape" value="3" /> 3</label></li> -<li><label><input type="checkbox" name="escape" value="bad" /> you & me</label></li> -<li><label><input type="checkbox" name="escape" value="good" /> you > me</label></li> -</ul>""") - - # Unicode choices are correctly rendered as HTML - self.assertHTMLEqual(w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>') - - # Each input gets a separate ID - self.assertHTMLEqual(CheckboxSelectMultiple().render('letters', list('ac'), choices=zip(list('abc'), list('ABC')), attrs={'id': 'abc'}), """<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>""") - - # Each input gets a separate ID when the ID is passed to the constructor - self.assertHTMLEqual(CheckboxSelectMultiple(attrs={'id': 'abc'}).render('letters', list('ac'), choices=zip(list('abc'), list('ABC'))), """<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>""") - - w = CheckboxSelectMultiple() - r = w.get_renderer('abc', 'b', choices=[(c, c.upper()) for c in 'abc']) - # You can iterate over the CheckboxFieldRenderer to get individual elements - expected = [ - '<label><input type="checkbox" name="abc" value="a" /> A</label>', - '<label><input checked="checked" type="checkbox" name="abc" value="b" /> B</label>', - '<label><input type="checkbox" name="abc" value="c" /> C</label>', - ] - for output, expected in zip(r, expected): - self.assertHTMLEqual(force_text(output), expected) - - # You can access individual elements - self.assertHTMLEqual(force_text(r[1]), - '<label><input checked="checked" type="checkbox" name="abc" value="b" /> B</label>') - - # Out-of-range errors are propagated - with self.assertRaises(IndexError): - r[42] - def test_subwidget(self): # Each subwidget tag gets a separate ID when the widget has an ID specified self.assertHTMLEqual("\n".join(c.tag() for c in CheckboxSelectMultiple(attrs={'id': 'abc'}).subwidgets('letters', list('ac'), choices=zip(list('abc'), list('ABC')))), """<input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" /> @@ -923,232 +122,6 @@ beatle J R Ringo False""") <input type="checkbox" name="letters" value="b" id="abc_1" /> <input type="checkbox" name="letters" value="c" id="abc_2" />""") - def test_multi(self): - class MyMultiWidget(MultiWidget): - def decompress(self, value): - if value: - return value.split('__') - return ['', ''] - - def format_output(self, rendered_widgets): - return '<br />'.join(rendered_widgets) - - w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'}))) - self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />') - self.assertHTMLEqual(w.render('name', 'john__lennon'), '<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />') - self.assertHTMLEqual(w.render('name', 'john__lennon', attrs={'id': 'foo'}), '<input id="foo_0" type="text" class="big" value="john" name="name_0" /><br /><input id="foo_1" type="text" class="small" value="lennon" name="name_1" />') - w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'}) - self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />') - - # Test needs_multipart_form=True if any widget needs it - w = MyMultiWidget(widgets=(TextInput(), FileInput())) - self.assertTrue(w.needs_multipart_form) - - # Test needs_multipart_form=False if no widget needs it - w = MyMultiWidget(widgets=(TextInput(), TextInput())) - self.assertFalse(w.needs_multipart_form) - - def test_splitdatetime(self): - w = SplitDateTimeWidget() - self.assertHTMLEqual(w.render('date', ''), '<input type="text" name="date_0" /><input type="text" name="date_1" />') - self.assertHTMLEqual(w.render('date', None), '<input type="text" name="date_0" /><input type="text" name="date_1" />') - self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />') - self.assertHTMLEqual(w.render('date', [datetime.date(2006, 1, 10), datetime.time(7, 30)]), '<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />') - - # You can also pass 'attrs' to the constructor. In this case, the attrs will be - w = SplitDateTimeWidget(attrs={'class': 'pretty'}) - self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />') - - # Use 'date_format' and 'time_format' to change the way a value is displayed. - w = SplitDateTimeWidget(date_format='%d/%m/%Y', time_format='%H:%M') - self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '<input type="text" name="date_0" value="10/01/2006" /><input type="text" name="date_1" value="07:30" />') - - def test_datetimeinput(self): - w = DateTimeInput() - self.assertHTMLEqual(w.render('date', None), '<input type="text" name="date" />') - d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) - self.assertEqual(str(d), '2007-09-17 12:51:34.482548') - - # The microseconds are trimmed on display, by default. - self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="2007-09-17 12:51:34" />') - self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34)), '<input type="text" name="date" value="2007-09-17 12:51:34" />') - self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '<input type="text" name="date" value="2007-09-17 12:51:00" />') - - # Use 'format' to change the way a value is displayed. - w = DateTimeInput(format='%d/%m/%Y %H:%M', attrs={'type': 'datetime'}) - self.assertHTMLEqual(w.render('date', d), '<input type="datetime" name="date" value="17/09/2007 12:51" />') - - def test_dateinput(self): - w = DateInput() - self.assertHTMLEqual(w.render('date', None), '<input type="text" name="date" />') - d = datetime.date(2007, 9, 17) - self.assertEqual(str(d), '2007-09-17') - - self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="2007-09-17" />') - self.assertHTMLEqual(w.render('date', datetime.date(2007, 9, 17)), '<input type="text" name="date" value="2007-09-17" />') - - # We should be able to initialize from a unicode value. - self.assertHTMLEqual(w.render('date', '2007-09-17'), '<input type="text" name="date" value="2007-09-17" />') - - # Use 'format' to change the way a value is displayed. - w = DateInput(format='%d/%m/%Y', attrs={'type': 'date'}) - self.assertHTMLEqual(w.render('date', d), '<input type="date" name="date" value="17/09/2007" />') - - def test_timeinput(self): - w = TimeInput() - self.assertHTMLEqual(w.render('time', None), '<input type="text" name="time" />') - t = datetime.time(12, 51, 34, 482548) - self.assertEqual(str(t), '12:51:34.482548') - - # The microseconds are trimmed on display, by default. - self.assertHTMLEqual(w.render('time', t), '<input type="text" name="time" value="12:51:34" />') - self.assertHTMLEqual(w.render('time', datetime.time(12, 51, 34)), '<input type="text" name="time" value="12:51:34" />') - self.assertHTMLEqual(w.render('time', datetime.time(12, 51)), '<input type="text" name="time" value="12:51:00" />') - - # We should be able to initialize from a unicode value. - self.assertHTMLEqual(w.render('time', '13:12:11'), '<input type="text" name="time" value="13:12:11" />') - - # Use 'format' to change the way a value is displayed. - w = TimeInput(format='%H:%M', attrs={'type': 'time'}) - self.assertHTMLEqual(w.render('time', t), '<input type="time" name="time" value="12:51" />') - - def test_splithiddendatetime(self): - from django.forms.widgets import SplitHiddenDateTimeWidget - - w = SplitHiddenDateTimeWidget() - self.assertHTMLEqual(w.render('date', ''), '<input type="hidden" name="date_0" /><input type="hidden" name="date_1" />') - d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) - self.assertHTMLEqual(str(d), '2007-09-17 12:51:34.482548') - self.assertHTMLEqual(w.render('date', d), '<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />') - self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34)), '<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />') - self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:00" />') - - def test_multiwidget(self): - # MultiWidgets are widgets composed of other widgets. They are usually - # combined with MultiValueFields - a field that is composed of other fields. - # MulitWidgets can themselves be composed of other MultiWidgets. - # SplitDateTimeWidget is one example of a MultiWidget. - - class ComplexMultiWidget(MultiWidget): - def __init__(self, attrs=None): - widgets = ( - TextInput(), - SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), - SplitDateTimeWidget(), - ) - super(ComplexMultiWidget, self).__init__(widgets, attrs) - - def decompress(self, value): - if value: - data = value.split(',') - return [data[0], list(data[1]), datetime.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) - - w = ComplexMultiWidget() - self.assertHTMLEqual( - w.render('name', 'some text,JP,2007-04-25 06:24:00'), - """ - <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" /> - """, - ) - - class ComplexField(MultiValueField): - def __init__(self, required=True, widget=None, label=None, initial=None): - fields = ( - CharField(), - MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), - 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 - - f = ComplexField(widget=w) - self.assertEqual( - f.clean(['some text', ['J', 'P'], ['2007-04-25', '6:24:00']]), - 'some text,JP,2007-04-25 06:24:00', - ) - - with self.assertRaisesMessage(ValidationError, - "'Select a valid choice. X is not one of the available choices.'"): - f.clean(['some text', ['X'], ['2007-04-25', '6:24:00']]) - - # If insufficient data is provided, None is substituted - self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, ['some text', ['JP']]) - - # test with no initial data - self.assertTrue(f.has_changed(None, ['some text', ['J', 'P'], ['2007-04-25', '6:24:00']])) - - # test when the data is the same as initial - self.assertFalse(f.has_changed('some text,JP,2007-04-25 06:24:00', - ['some text', ['J', 'P'], ['2007-04-25', '6:24:00']])) - - # test when the first widget's data has changed - self.assertTrue(f.has_changed('some text,JP,2007-04-25 06:24:00', - ['other text', ['J', 'P'], ['2007-04-25', '6:24:00']])) - - # test when the last widget's data has changed. this ensures that it is not - # short circuiting while testing the widgets. - self.assertTrue(f.has_changed('some text,JP,2007-04-25 06:24:00', - ['some text', ['J', 'P'], ['2009-04-25', '11:44:00']])) - - class ComplexFieldForm(Form): - field1 = ComplexField(widget=w) - - f = ComplexFieldForm() - self.assertHTMLEqual( - f.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> - """, - ) - - f = ComplexFieldForm({ - 'field1_0': 'some text', - 'field1_1': ['J', 'P'], - 'field1_2_0': '2007-04-25', - 'field1_2_1': '06:24:00', - }) - self.assertHTMLEqual( - f.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> - """, - ) - self.assertEqual(f.cleaned_data['field1'], 'some text,JP,2007-04-25 06:24:00') - def test_sub_widget_html_safe(self): widget = TextInput() subwidget = next(widget.subwidgets('username', 'John Doe')) @@ -1167,111 +140,6 @@ beatle J R Ringo False""") self.assertEqual(force_text(renderer), renderer.__html__()) -class NullBooleanSelectLazyForm(Form): - """Form to test for lazy evaluation. Refs #17190""" - bool = BooleanField(widget=NullBooleanSelect()) - - -@override_settings(USE_L10N=True) -class FormsI18NWidgetsTests(SimpleTestCase): - def setUp(self): - super(FormsI18NWidgetsTests, self).setUp() - translation.activate('de-at') - - def tearDown(self): - translation.deactivate() - super(FormsI18NWidgetsTests, self).tearDown() - - def test_datetimeinput(self): - w = DateTimeInput() - d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) - self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17.09.2007 12:51:34" />') - - def test_dateinput(self): - w = DateInput() - d = datetime.date(2007, 9, 17) - self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17.09.2007" />') - - def test_timeinput(self): - w = TimeInput() - t = datetime.time(12, 51, 34, 482548) - self.assertHTMLEqual(w.render('time', t), '<input type="text" name="time" value="12:51:34" />') - - def test_datetime_locale_aware(self): - w = DateTimeInput() - d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) - with self.settings(USE_L10N=False): - self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="2007-09-17 12:51:34" />') - with translation.override('es'): - self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17/09/2007 12:51:34" />') - - def test_splithiddendatetime(self): - from django.forms.widgets import SplitHiddenDateTimeWidget - - w = SplitHiddenDateTimeWidget() - self.assertHTMLEqual( - w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), - """ - <input type="hidden" name="date_0" value="17.09.2007" /> - <input type="hidden" name="date_1" value="12:51:00" /> - """, - ) - - def test_nullbooleanselect(self): - """ - Ensure that the NullBooleanSelect widget's options are lazily - localized. - Refs #17190 - """ - f = NullBooleanSelectLazyForm() - self.assertHTMLEqual( - f.fields['bool'].widget.render('id_bool', True), - """ - <select name="id_bool"> - <option value="1">Unbekannt</option> - <option value="2" selected="selected">Ja</option> - <option value="3">Nein</option> - </select> - """, - ) - - -class SelectAndTextWidget(MultiWidget): - """ - MultiWidget subclass - """ - def __init__(self, choices=[]): - widgets = [ - RadioSelect(choices=choices), - TextInput - ] - super(SelectAndTextWidget, 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 WidgetTests(SimpleTestCase): - def test_12048(self): - # See ticket #12048. - w1 = SelectAndTextWidget(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]) - - @override_settings(ROOT_URLCONF='forms_tests.urls') class LiveWidgetTests(AdminSeleniumWebDriverTestCase): @@ -1303,114 +171,6 @@ class FakeFieldFile(object): class ClearableFileInputTests(SimpleTestCase): - 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. - """ - widget = ClearableFileInput() - widget.is_required = False - self.assertHTMLEqual( - widget.render('myfile', FakeFieldFile()), - """ - 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. Refs #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.assertHTMLEqual( - widget.render('myfile', FakeFieldFile()), - """ - 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. - """ - widget = ClearableFileInput() - widget.is_required = False - self.assertHTMLEqual(widget.render('myfile', None), - '<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. - """ - widget = ClearableFileInput() - widget.is_required = False - self.assertEqual(widget.value_from_datadict( - data={'myfile-clear': True}, - files={}, - name='myfile'), 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 - f = SimpleUploadedFile('something.txt', b'content') - self.assertEqual(widget.value_from_datadict( - data={'myfile-clear': True}, - files={'myfile': f}, - name='myfile'), f) def test_render_custom_template(self): widget = ClearableFileInput() @@ -1424,585 +184,3 @@ class ClearableFileInputTests(SimpleTestCase): '<input type="checkbox" name="myfile-clear" id="myfile-clear_id" /> ' '<label for="myfile-clear_id">Clear</label><br />Change: <input type="file" name="myfile" />' ) - - -class GetDate(Form): - mydate = DateField(widget=SelectDateWidget) - - -class SelectDateWidgetTests(SimpleTestCase): - - # The forms library comes with some extra, higher-level Field and Widget - def test_selectdate(self): - self.maxDiff = None - w = SelectDateWidget(years=('2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016')) - - # Rendering the default state. - self.assertHTMLEqual( - w.render('mydate', ''), - """ - <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> - """, - ) - - # Rendering the None or '' values should yield the same output. - self.assertHTMLEqual(w.render('mydate', None), w.render('mydate', '')) - - # Rendering a string value. - self.assertHTMLEqual( - w.render('mydate', '2010-04-15'), - """ - <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> - """, - ) - - # Rendering a datetime value. - self.assertHTMLEqual(w.render('mydate', datetime.date(2010, 4, 15)), w.render('mydate', '2010-04-15')) - - # Invalid dates should still render the failed date. - self.assertHTMLEqual( - w.render('mydate', '2010-02-31'), - """ - <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> - """, - ) - - # Rendering with a custom months dict. - w = SelectDateWidget(months=MONTHS_AP, years=('2013',)) - self.assertHTMLEqual( - w.render('mydate', ''), - """ - <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> - """, - ) - - a = GetDate({'mydate_month': '4', 'mydate_day': '1', 'mydate_year': '2008'}) - self.assertTrue(a.is_valid()) - self.assertEqual(a.cleaned_data['mydate'], datetime.date(2008, 4, 1)) - - # As with any widget that implements get_value_from_datadict, - # we must be prepared to 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'], datetime.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()) - - 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 work - w = SelectDateWidget(years=('1899',)) - self.assertEqual( - w.value_from_datadict({'date_year': '1899', 'date_month': '8', 'date_day': '13'}, {}, 'date'), - '13-08-1899', - ) - - @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. - Refs #17165. - """ - # With Field.show_hidden_initial=False ----------------------- - b = GetDate({ - 'mydate_year': '2008', - 'mydate_month': '4', - 'mydate_day': '1', - }, initial={'mydate': datetime.date(2008, 4, 1)}) - self.assertFalse(b.has_changed()) - - b = GetDate({ - 'mydate_year': '2008', - 'mydate_month': '4', - 'mydate_day': '2', - }, initial={'mydate': datetime.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(datetime.date(2008, 4, 1)) - }, initial={'mydate': datetime.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(datetime.date(2008, 4, 1)) - }, initial={'mydate': datetime.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(datetime.date(2008, 4, 1)) - }, initial={'mydate': datetime.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(datetime.date(2008, 4, 22)) - }, initial={'mydate': datetime.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()) - - -class SelectWidgetTests(SimpleTestCase): - - 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) diff --git a/tests/forms_tests/widget_tests/__init__.py b/tests/forms_tests/widget_tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/forms_tests/widget_tests/__init__.py diff --git a/tests/forms_tests/widget_tests/base.py b/tests/forms_tests/widget_tests/base.py new file mode 100644 index 0000000000..3127cb85f2 --- /dev/null +++ b/tests/forms_tests/widget_tests/base.py @@ -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) diff --git a/tests/forms_tests/widget_tests/test_checkboxinput.py b/tests/forms_tests/widget_tests/test_checkboxinput.py new file mode 100644 index 0000000000..45d8191f75 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_checkboxinput.py @@ -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) diff --git a/tests/forms_tests/widget_tests/test_checkboxselectmultiple.py b/tests/forms_tests/widget_tests/test_checkboxselectmultiple.py new file mode 100644 index 0000000000..9e594fba37 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_checkboxselectmultiple.py @@ -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) diff --git a/tests/forms_tests/widget_tests/test_clearablefileinput.py b/tests/forms_tests/widget_tests/test_clearablefileinput.py new file mode 100644 index 0000000000..5a24a85e2a --- /dev/null +++ b/tests/forms_tests/widget_tests/test_clearablefileinput.py @@ -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) diff --git a/tests/forms_tests/widget_tests/test_dateinput.py b/tests/forms_tests/widget_tests/test_dateinput.py new file mode 100644 index 0000000000..3cf15fed8a --- /dev/null +++ b/tests/forms_tests/widget_tests/test_dateinput.py @@ -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" />', + ) diff --git a/tests/forms_tests/widget_tests/test_datetimeinput.py b/tests/forms_tests/widget_tests/test_datetimeinput.py new file mode 100644 index 0000000000..50fd7f5442 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_datetimeinput.py @@ -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" />', + ) diff --git a/tests/forms_tests/widget_tests/test_fileinput.py b/tests/forms_tests/widget_tests/test_fileinput.py new file mode 100644 index 0000000000..eb1c9d81ed --- /dev/null +++ b/tests/forms_tests/widget_tests/test_fileinput.py @@ -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" />') diff --git a/tests/forms_tests/widget_tests/test_hiddeninput.py b/tests/forms_tests/widget_tests/test_hiddeninput.py new file mode 100644 index 0000000000..039e89d5cc --- /dev/null +++ b/tests/forms_tests/widget_tests/test_hiddeninput.py @@ -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" />') diff --git a/tests/forms_tests/widget_tests/test_multiplehiddeninput.py b/tests/forms_tests/widget_tests/test_multiplehiddeninput.py new file mode 100644 index 0000000000..d1c1ae6fa5 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_multiplehiddeninput.py @@ -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" />' + ), + ) diff --git a/tests/forms_tests/widget_tests/test_multiwidget.py b/tests/forms_tests/widget_tests/test_multiwidget.py new file mode 100644 index 0000000000..bb6f8bfc4f --- /dev/null +++ b/tests/forms_tests/widget_tests/test_multiwidget.py @@ -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]) diff --git a/tests/forms_tests/widget_tests/test_nullbooleanselect.py b/tests/forms_tests/widget_tests/test_nullbooleanselect.py new file mode 100644 index 0000000000..42b1dbf7c1 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_nullbooleanselect.py @@ -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> + """ + )) diff --git a/tests/forms_tests/widget_tests/test_passwordinput.py b/tests/forms_tests/widget_tests/test_passwordinput.py new file mode 100644 index 0000000000..4cb7c4ef47 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_passwordinput.py @@ -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" />', + ) diff --git a/tests/forms_tests/widget_tests/test_radioselect.py b/tests/forms_tests/widget_tests/test_radioselect.py new file mode 100644 index 0000000000..e37dcf7725 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_radioselect.py @@ -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) diff --git a/tests/forms_tests/widget_tests/test_select.py b/tests/forms_tests/widget_tests/test_select.py new file mode 100644 index 0000000000..86ef355c62 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_select.py @@ -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) diff --git a/tests/forms_tests/widget_tests/test_selectdatewidget.py b/tests/forms_tests/widget_tests/test_selectdatewidget.py new file mode 100644 index 0000000000..e95c083532 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_selectdatewidget.py @@ -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', + ) diff --git a/tests/forms_tests/widget_tests/test_selectmultiple.py b/tests/forms_tests/widget_tests/test_selectmultiple.py new file mode 100644 index 0000000000..502aba3bf4 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_selectmultiple.py @@ -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>""" + )) diff --git a/tests/forms_tests/widget_tests/test_splitdatetimewidget.py b/tests/forms_tests/widget_tests/test_splitdatetimewidget.py new file mode 100644 index 0000000000..172bcbbe8d --- /dev/null +++ b/tests/forms_tests/widget_tests/test_splitdatetimewidget.py @@ -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" />' + )) diff --git a/tests/forms_tests/widget_tests/test_splithiddendatetimewidget.py b/tests/forms_tests/widget_tests/test_splithiddendatetimewidget.py new file mode 100644 index 0000000000..07ee15690c --- /dev/null +++ b/tests/forms_tests/widget_tests/test_splithiddendatetimewidget.py @@ -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" /> + """ + )) diff --git a/tests/forms_tests/widget_tests/test_textarea.py b/tests/forms_tests/widget_tests/test_textarea.py new file mode 100644 index 0000000000..490848fab3 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_textarea.py @@ -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>' + )) diff --git a/tests/forms_tests/widget_tests/test_textinput.py b/tests/forms_tests/widget_tests/test_textinput.py new file mode 100644 index 0000000000..33e455a160 --- /dev/null +++ b/tests/forms_tests/widget_tests/test_textinput.py @@ -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" />') diff --git a/tests/forms_tests/widget_tests/test_timeinput.py b/tests/forms_tests/widget_tests/test_timeinput.py new file mode 100644 index 0000000000..96fb04e24c --- /dev/null +++ b/tests/forms_tests/widget_tests/test_timeinput.py @@ -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" />') |
