From 3a4c9e1b43ff67b6cf4c59da757666d6ac5ce4a0 Mon Sep 17 00:00:00 2001 From: Loic Bistuer <loic.bistuer@gmail.com> Date: Sun, 25 Jan 2015 22:45:54 +0700 Subject: [PATCH] Cleaned up some forms tests. Thanks Berker Peksag and Tim Graham for the reviews. Refs #24219. --- tests/forms_tests/tests/test_extra.py | 844 ------------------------ tests/forms_tests/tests/test_fields.py | 104 ++- tests/forms_tests/tests/test_forms.py | 99 ++- tests/forms_tests/tests/test_widgets.py | 761 ++++++++++++++++++++- tests/utils_tests/test_encoding.py | 44 +- 5 files changed, 969 insertions(+), 883 deletions(-) delete mode 100644 tests/forms_tests/tests/test_extra.py diff --git a/tests/forms_tests/tests/test_extra.py b/tests/forms_tests/tests/test_extra.py deleted file mode 100644 index ababa689de..0000000000 --- a/tests/forms_tests/tests/test_extra.py +++ /dev/null @@ -1,844 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import datetime - -from django.forms import ( - CharField, DateField, EmailField, FileField, Form, GenericIPAddressField, - HiddenInput, ImageField, MultipleChoiceField, MultiValueField, MultiWidget, - PasswordInput, SelectMultiple, SlugField, SplitDateTimeField, - SplitDateTimeWidget, TextInput, URLField, -) -from django.forms.extras import SelectDateWidget -from django.forms.utils import ErrorList -from django.test import TestCase, override_settings -from django.utils import six -from django.utils import translation -from django.utils.dates import MONTHS_AP -from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible - -from .test_error_messages import AssertFormErrorsMixin - - -class GetDate(Form): - mydate = DateField(widget=SelectDateWidget) - - -class GetDateShowHiddenInitial(Form): - mydate = DateField(widget=SelectDateWidget, show_hidden_initial=True) - - -class FormsExtraTestCase(TestCase, AssertFormErrorsMixin): - ############### - # Extra stuff # - ############### - - # 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_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')) - - def test_multiwidget(self): - # MultiWidget and MultiValueField ############################################# - # 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') - self.assertFormErrors(['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.assertFormErrors(['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_generic_ipaddress_invalid_arguments(self): - self.assertRaises(ValueError, GenericIPAddressField, protocol="hamster") - self.assertRaises(ValueError, GenericIPAddressField, protocol="ipv4", unpack_ipv4=True) - - def test_generic_ipaddress_as_generic(self): - # The edge cases of the IPv6 validation code are not deeply tested - # here, they are covered in the tests for django.utils.ipv6 - f = GenericIPAddressField() - self.assertFormErrors(['This field is required.'], f.clean, '') - self.assertFormErrors(['This field is required.'], f.clean, None) - self.assertEqual(f.clean(' 127.0.0.1 '), '127.0.0.1') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, '127.0.0.') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, '1.2.3.4.5') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, '256.125.1.5') - self.assertEqual(f.clean(' fe80::223:6cff:fe8a:2e8a '), 'fe80::223:6cff:fe8a:2e8a') - self.assertEqual(f.clean(' 2a02::223:6cff:fe8a:2e8a '), '2a02::223:6cff:fe8a:2e8a') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '12345:2:3:4') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1::2:3::4') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1:2') - - def test_generic_ipaddress_as_ipv4_only(self): - f = GenericIPAddressField(protocol="IPv4") - self.assertFormErrors(['This field is required.'], f.clean, '') - self.assertFormErrors(['This field is required.'], f.clean, None) - self.assertEqual(f.clean(' 127.0.0.1 '), '127.0.0.1') - self.assertFormErrors(['Enter a valid IPv4 address.'], f.clean, 'foo') - self.assertFormErrors(['Enter a valid IPv4 address.'], f.clean, '127.0.0.') - self.assertFormErrors(['Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5') - self.assertFormErrors(['Enter a valid IPv4 address.'], f.clean, '256.125.1.5') - self.assertFormErrors(['Enter a valid IPv4 address.'], f.clean, 'fe80::223:6cff:fe8a:2e8a') - self.assertFormErrors(['Enter a valid IPv4 address.'], f.clean, '2a02::223:6cff:fe8a:2e8a') - - def test_generic_ipaddress_as_ipv6_only(self): - f = GenericIPAddressField(protocol="IPv6") - self.assertFormErrors(['This field is required.'], f.clean, '') - self.assertFormErrors(['This field is required.'], f.clean, None) - self.assertFormErrors(['Enter a valid IPv6 address.'], f.clean, '127.0.0.1') - self.assertFormErrors(['Enter a valid IPv6 address.'], f.clean, 'foo') - self.assertFormErrors(['Enter a valid IPv6 address.'], f.clean, '127.0.0.') - self.assertFormErrors(['Enter a valid IPv6 address.'], f.clean, '1.2.3.4.5') - self.assertFormErrors(['Enter a valid IPv6 address.'], f.clean, '256.125.1.5') - self.assertEqual(f.clean(' fe80::223:6cff:fe8a:2e8a '), 'fe80::223:6cff:fe8a:2e8a') - self.assertEqual(f.clean(' 2a02::223:6cff:fe8a:2e8a '), '2a02::223:6cff:fe8a:2e8a') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '12345:2:3:4') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1::2:3::4') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1:2') - - def test_generic_ipaddress_as_generic_not_required(self): - f = GenericIPAddressField(required=False) - self.assertEqual(f.clean(''), '') - self.assertEqual(f.clean(None), '') - self.assertEqual(f.clean('127.0.0.1'), '127.0.0.1') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, '127.0.0.') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, '1.2.3.4.5') - self.assertFormErrors(['Enter a valid IPv4 or IPv6 address.'], f.clean, '256.125.1.5') - self.assertEqual(f.clean(' fe80::223:6cff:fe8a:2e8a '), 'fe80::223:6cff:fe8a:2e8a') - self.assertEqual(f.clean(' 2a02::223:6cff:fe8a:2e8a '), '2a02::223:6cff:fe8a:2e8a') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '12345:2:3:4') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1::2:3::4') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8') - self.assertFormErrors(['This is not a valid IPv6 address.'], f.clean, '1:2') - - def test_generic_ipaddress_normalization(self): - # Test the normalizing code - f = GenericIPAddressField() - self.assertEqual(f.clean(' ::ffff:0a0a:0a0a '), '::ffff:10.10.10.10') - self.assertEqual(f.clean(' ::ffff:10.10.10.10 '), '::ffff:10.10.10.10') - self.assertEqual(f.clean(' 2001:000:a:0000:0:fe:fe:beef '), '2001:0:a::fe:fe:beef') - self.assertEqual(f.clean(' 2001::a:0000:0:fe:fe:beef '), '2001:0:a::fe:fe:beef') - - f = GenericIPAddressField(unpack_ipv4=True) - self.assertEqual(f.clean(' ::ffff:0a0a:0a0a'), '10.10.10.10') - - def test_slugfield_normalization(self): - f = SlugField() - self.assertEqual(f.clean(' aa-bb-cc '), 'aa-bb-cc') - - def test_urlfield_normalization(self): - f = URLField() - self.assertEqual(f.clean('http://example.com/ '), 'http://example.com/') - - def test_smart_text(self): - class Test: - if six.PY3: - def __str__(self): - return 'ŠĐĆŽćžšđ' - else: - def __str__(self): - return 'ŠĐĆŽćžšđ'.encode('utf-8') - - class TestU: - if six.PY3: - def __str__(self): - return 'ŠĐĆŽćžšđ' - - def __bytes__(self): - return b'Foo' - else: - def __str__(self): - return b'Foo' - - def __unicode__(self): - return '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' - - self.assertEqual(smart_text(Test()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') - self.assertEqual(smart_text(TestU()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') - self.assertEqual(smart_text(1), '1') - self.assertEqual(smart_text('foo'), 'foo') - - def test_accessing_clean(self): - class UserForm(Form): - username = CharField(max_length=10) - password = CharField(widget=PasswordInput) - - def clean(self): - data = self.cleaned_data - - if not self.errors: - data['username'] = data['username'].lower() - - return data - - f = UserForm({'username': 'SirRobin', 'password': 'blue'}) - self.assertTrue(f.is_valid()) - self.assertEqual(f.cleaned_data['username'], 'sirrobin') - - def test_changing_cleaned_data_nothing_returned(self): - class UserForm(Form): - username = CharField(max_length=10) - password = CharField(widget=PasswordInput) - - def clean(self): - self.cleaned_data['username'] = self.cleaned_data['username'].lower() - # don't return anything - - f = UserForm({'username': 'SirRobin', 'password': 'blue'}) - self.assertTrue(f.is_valid()) - self.assertEqual(f.cleaned_data['username'], 'sirrobin') - - def test_changing_cleaned_data_in_clean(self): - class UserForm(Form): - username = CharField(max_length=10) - password = CharField(widget=PasswordInput) - - def clean(self): - data = self.cleaned_data - - # Return a different dict. We have not changed self.cleaned_data. - return { - 'username': data['username'].lower(), - 'password': 'this_is_not_a_secret', - } - - f = UserForm({'username': 'SirRobin', 'password': 'blue'}) - self.assertTrue(f.is_valid()) - self.assertEqual(f.cleaned_data['username'], 'sirrobin') - - def test_overriding_errorlist(self): - @python_2_unicode_compatible - class DivErrorList(ErrorList): - def __str__(self): - return self.as_divs() - - def as_divs(self): - if not self: - return '' - return '<div class="errorlist">%s</div>' % ''.join('<div class="error">%s</div>' % force_text(e) for e in self) - - class CommentForm(Form): - name = CharField(max_length=50, required=False) - email = EmailField() - comment = CharField() - - data = dict(email='invalid') - f = CommentForm(data, auto_id=False, error_class=DivErrorList) - self.assertHTMLEqual(f.as_p(), """<p>Name: <input type="text" name="name" maxlength="50" /></p> -<div class="errorlist"><div class="error">Enter a valid email address.</div></div> -<p>Email: <input type="email" name="email" value="invalid" /></p> -<div class="errorlist"><div class="error">This field is required.</div></div> -<p>Comment: <input type="text" name="comment" /></p>""") - - def test_multipart_encoded_form(self): - class FormWithoutFile(Form): - username = CharField() - - class FormWithFile(Form): - username = CharField() - file = FileField() - - class FormWithImage(Form): - image = ImageField() - - self.assertFalse(FormWithoutFile().is_multipart()) - self.assertTrue(FormWithFile().is_multipart()) - self.assertTrue(FormWithImage().is_multipart()) - - def test_selectdatewidget_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) - - -@override_settings(USE_L10N=True) -class FormsExtraL10NTestCase(TestCase): - def setUp(self): - super(FormsExtraL10NTestCase, self).setUp() - translation.activate('nl') - - def tearDown(self): - translation.deactivate() - super(FormsExtraL10NTestCase, self).tearDown() - - 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>""") - - # 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') - - 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 ------------------------ - 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()) - - 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.']}) - - 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/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py index 003df1c3a2..fda451281f 100644 --- a/tests/forms_tests/tests/test_fields.py +++ b/tests/forms_tests/tests/test_fields.py @@ -43,11 +43,11 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import ( BooleanField, CharField, ChoiceField, ComboField, DateField, DateTimeField, DecimalField, DurationField, EmailField, Field, FileField, FilePathField, - FloatField, Form, forms, HiddenInput, ImageField, IntegerField, - MultipleChoiceField, NullBooleanField, NumberInput, PasswordInput, - RadioSelect, RegexField, SplitDateTimeField, TextInput, Textarea, - TimeField, TypedChoiceField, TypedMultipleChoiceField, URLField, UUIDField, - ValidationError, Widget, + FloatField, Form, forms, GenericIPAddressField, HiddenInput, ImageField, + IntegerField, MultipleChoiceField, NullBooleanField, NumberInput, + PasswordInput, RadioSelect, RegexField, SlugField, SplitDateTimeField, + TextInput, Textarea, TimeField, TypedChoiceField, TypedMultipleChoiceField, + URLField, UUIDField, ValidationError, Widget, ) from django.test import SimpleTestCase, ignore_warnings from django.utils import formats @@ -906,6 +906,10 @@ class FieldsTests(SimpleTestCase): f = URLField(required=False) self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 23) + def test_urlfield_normalization(self): + f = URLField() + self.assertEqual(f.clean('http://example.com/ '), 'http://example.com/') + # BooleanField ################################################################ def test_booleanfield_1(self): @@ -1385,6 +1389,96 @@ class FieldsTests(SimpleTestCase): self.assertFalse(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:40'])) self.assertTrue(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:41'])) + # GenericIPAddressField ####################################################### + + def test_generic_ipaddress_invalid_arguments(self): + self.assertRaises(ValueError, GenericIPAddressField, protocol="hamster") + self.assertRaises(ValueError, GenericIPAddressField, protocol="ipv4", unpack_ipv4=True) + + def test_generic_ipaddress_as_generic(self): + # The edge cases of the IPv6 validation code are not deeply tested + # here, they are covered in the tests for django.utils.ipv6 + f = GenericIPAddressField() + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) + self.assertEqual(f.clean(' 127.0.0.1 '), '127.0.0.1') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, 'foo') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, '127.0.0.') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, '1.2.3.4.5') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, '256.125.1.5') + self.assertEqual(f.clean(' fe80::223:6cff:fe8a:2e8a '), 'fe80::223:6cff:fe8a:2e8a') + self.assertEqual(f.clean(' 2a02::223:6cff:fe8a:2e8a '), '2a02::223:6cff:fe8a:2e8a') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '12345:2:3:4') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1::2:3::4') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, 'foo::223:6cff:fe8a:2e8a') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1::2:3:4:5:6:7:8') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1:2') + + def test_generic_ipaddress_as_ipv4_only(self): + f = GenericIPAddressField(protocol="IPv4") + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) + self.assertEqual(f.clean(' 127.0.0.1 '), '127.0.0.1') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 address.'", f.clean, 'foo') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 address.'", f.clean, '127.0.0.') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 address.'", f.clean, '1.2.3.4.5') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 address.'", f.clean, '256.125.1.5') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 address.'", f.clean, 'fe80::223:6cff:fe8a:2e8a') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 address.'", f.clean, '2a02::223:6cff:fe8a:2e8a') + + def test_generic_ipaddress_as_ipv6_only(self): + f = GenericIPAddressField(protocol="IPv6") + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv6 address.'", f.clean, '127.0.0.1') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv6 address.'", f.clean, 'foo') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv6 address.'", f.clean, '127.0.0.') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv6 address.'", f.clean, '1.2.3.4.5') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv6 address.'", f.clean, '256.125.1.5') + self.assertEqual(f.clean(' fe80::223:6cff:fe8a:2e8a '), 'fe80::223:6cff:fe8a:2e8a') + self.assertEqual(f.clean(' 2a02::223:6cff:fe8a:2e8a '), '2a02::223:6cff:fe8a:2e8a') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '12345:2:3:4') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1::2:3::4') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, 'foo::223:6cff:fe8a:2e8a') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1::2:3:4:5:6:7:8') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1:2') + + def test_generic_ipaddress_as_generic_not_required(self): + f = GenericIPAddressField(required=False) + self.assertEqual(f.clean(''), '') + self.assertEqual(f.clean(None), '') + self.assertEqual(f.clean('127.0.0.1'), '127.0.0.1') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, 'foo') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, '127.0.0.') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, '1.2.3.4.5') + self.assertRaisesMessage(ValidationError, "'Enter a valid IPv4 or IPv6 address.'", f.clean, '256.125.1.5') + self.assertEqual(f.clean(' fe80::223:6cff:fe8a:2e8a '), 'fe80::223:6cff:fe8a:2e8a') + self.assertEqual(f.clean(' 2a02::223:6cff:fe8a:2e8a '), '2a02::223:6cff:fe8a:2e8a') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '12345:2:3:4') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1::2:3::4') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, 'foo::223:6cff:fe8a:2e8a') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1::2:3:4:5:6:7:8') + self.assertRaisesMessage(ValidationError, "'This is not a valid IPv6 address.'", f.clean, '1:2') + + def test_generic_ipaddress_normalization(self): + # Test the normalizing code + f = GenericIPAddressField() + self.assertEqual(f.clean(' ::ffff:0a0a:0a0a '), '::ffff:10.10.10.10') + self.assertEqual(f.clean(' ::ffff:10.10.10.10 '), '::ffff:10.10.10.10') + self.assertEqual(f.clean(' 2001:000:a:0000:0:fe:fe:beef '), '2001:0:a::fe:fe:beef') + self.assertEqual(f.clean(' 2001::a:0000:0:fe:fe:beef '), '2001:0:a::fe:fe:beef') + + f = GenericIPAddressField(unpack_ipv4=True) + self.assertEqual(f.clean(' ::ffff:0a0a:0a0a'), '10.10.10.10') + + # SlugField ################################################################### + + def test_slugfield_normalization(self): + f = SlugField() + self.assertEqual(f.clean(' aa-bb-cc '), 'aa-bb-cc') + + # UUIDField ################################################################### + def test_uuidfield_1(self): field = UUIDField() value = field.clean('550e8400e29b41d4a716446655440000') diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index d938694e8f..df090ef60e 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -11,9 +11,10 @@ from django.core.validators import RegexValidator from django.forms import ( BooleanField, CharField, CheckboxSelectMultiple, ChoiceField, DateField, DateTimeField, EmailField, FileField, FloatField, Form, forms, HiddenInput, - IntegerField, MultipleChoiceField, MultipleHiddenInput, MultiValueField, - NullBooleanField, PasswordInput, RadioSelect, Select, SplitDateTimeField, - Textarea, TextInput, TimeField, ValidationError, widgets + ImageField, IntegerField, MultipleChoiceField, MultipleHiddenInput, + MultiValueField, NullBooleanField, PasswordInput, RadioSelect, Select, + SplitDateTimeField, SplitHiddenDateTimeWidget, Textarea, TextInput, + TimeField, ValidationError, ) from django.forms.utils import ErrorList from django.http import QueryDict @@ -21,7 +22,7 @@ from django.template import Template, Context from django.test import TestCase from django.test.utils import str_prefix from django.utils.datastructures import MultiValueDict -from django.utils.encoding import force_text +from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.html import format_html from django.utils.safestring import mark_safe, SafeData from django.utils import six @@ -1945,7 +1946,7 @@ class FormsTestCase(TestCase): def test_label_split_datetime_not_displayed(self): class EventForm(Form): - happened_at = SplitDateTimeField(widget=widgets.SplitHiddenDateTimeWidget) + happened_at = SplitDateTimeField(widget=SplitHiddenDateTimeWidget) form = EventForm() self.assertHTMLEqual(form.as_ul(), '<input type="hidden" name="happened_at_0" id="id_happened_at_0" /><input type="hidden" name="happened_at_1" id="id_happened_at_1" />') @@ -2399,6 +2400,31 @@ class FormsTestCase(TestCase): <tr><th><label for="id_last_name">Last name:</label></th><td><input id="id_last_name" name="last_name" type="text" value="Lennon" /></td></tr>""" ) + def test_errorlist_override(self): + @python_2_unicode_compatible + class DivErrorList(ErrorList): + def __str__(self): + return self.as_divs() + + def as_divs(self): + if not self: + return '' + return '<div class="errorlist">%s</div>' % ''.join( + '<div class="error">%s</div>' % force_text(e) for e in self) + + class CommentForm(Form): + name = CharField(max_length=50, required=False) + email = EmailField() + comment = CharField() + + data = dict(email='invalid') + f = CommentForm(data, auto_id=False, error_class=DivErrorList) + self.assertHTMLEqual(f.as_p(), """<p>Name: <input type="text" name="name" maxlength="50" /></p> +<div class="errorlist"><div class="error">Enter a valid email address.</div></div> +<p>Email: <input type="email" name="email" value="invalid" /></p> +<div class="errorlist"><div class="error">This field is required.</div></div> +<p>Comment: <input type="text" name="comment" /></p>""") + def test_baseform_repr(self): """ BaseForm.__repr__() should contain some basic information about the @@ -2423,3 +2449,66 @@ class FormsTestCase(TestCase): self.assertRaises(AttributeError, lambda: p.cleaned_data) self.assertFalse(p.is_valid()) self.assertEqual(p.cleaned_data, {'first_name': 'John', 'last_name': 'Lennon'}) + + def test_accessing_clean(self): + class UserForm(Form): + username = CharField(max_length=10) + password = CharField(widget=PasswordInput) + + def clean(self): + data = self.cleaned_data + + if not self.errors: + data['username'] = data['username'].lower() + + return data + + f = UserForm({'username': 'SirRobin', 'password': 'blue'}) + self.assertTrue(f.is_valid()) + self.assertEqual(f.cleaned_data['username'], 'sirrobin') + + def test_changing_cleaned_data_nothing_returned(self): + class UserForm(Form): + username = CharField(max_length=10) + password = CharField(widget=PasswordInput) + + def clean(self): + self.cleaned_data['username'] = self.cleaned_data['username'].lower() + # don't return anything + + f = UserForm({'username': 'SirRobin', 'password': 'blue'}) + self.assertTrue(f.is_valid()) + self.assertEqual(f.cleaned_data['username'], 'sirrobin') + + def test_changing_cleaned_data_in_clean(self): + class UserForm(Form): + username = CharField(max_length=10) + password = CharField(widget=PasswordInput) + + def clean(self): + data = self.cleaned_data + + # Return a different dict. We have not changed self.cleaned_data. + return { + 'username': data['username'].lower(), + 'password': 'this_is_not_a_secret', + } + + f = UserForm({'username': 'SirRobin', 'password': 'blue'}) + self.assertTrue(f.is_valid()) + self.assertEqual(f.cleaned_data['username'], 'sirrobin') + + def test_multipart_encoded_form(self): + class FormWithoutFile(Form): + username = CharField() + + class FormWithFile(Form): + username = CharField() + file = FileField() + + class FormWithImage(Form): + image = ImageField() + + self.assertFalse(FormWithoutFile().is_multipart()) + self.assertTrue(FormWithFile().is_multipart()) + self.assertTrue(FormWithImage().is_multipart()) diff --git a/tests/forms_tests/tests/test_widgets.py b/tests/forms_tests/tests/test_widgets.py index efaefe1180..dd8a9b4030 100644 --- a/tests/forms_tests/tests/test_widgets.py +++ b/tests/forms_tests/tests/test_widgets.py @@ -8,23 +8,25 @@ 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, CheckboxInput, CheckboxSelectMultiple, ChoiceField, - ClearableFileInput, DateInput, DateTimeInput, FileInput, - Form, HiddenInput, MultipleHiddenInput, MultiWidget, NullBooleanSelect, - PasswordInput, RadioSelect, Select, SelectMultiple, SplitDateTimeWidget, - Textarea, TextInput, TimeInput, + BooleanField, CharField, CheckboxInput, CheckboxSelectMultiple, + ChoiceField, ClearableFileInput, DateField, DateInput, DateTimeInput, + FileInput, Form, HiddenInput, MultipleChoiceField, MultipleHiddenInput, + MultiValueField, MultiWidget, NullBooleanSelect, PasswordInput, + RadioSelect, Select, SelectMultiple, SplitDateTimeField, + SplitDateTimeWidget, Textarea, TextInput, TimeInput, ValidationError, ) +from django.forms.extras import SelectDateWidget from django.forms.widgets import RadioFieldRenderer from django.utils.safestring import mark_safe, SafeData -from django.utils import six -from django.utils.translation import activate, deactivate, override +from django.utils import six, translation from django.test import TestCase, override_settings -from django.utils.encoding import python_2_unicode_compatible, force_text +from django.utils.dates import MONTHS_AP +from django.utils.encoding import force_text, python_2_unicode_compatible from ..models import Article -class FormsWidgetTestCase(TestCase): +class FormsWidgetTests(TestCase): # 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. @@ -1020,6 +1022,132 @@ beatle J R Ringo False""") 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') class NullBooleanSelectLazyForm(Form): """Form to test for lazy evaluation. Refs #17190""" @@ -1027,14 +1155,14 @@ class NullBooleanSelectLazyForm(Form): @override_settings(USE_L10N=True) -class FormsI18NWidgetsTestCase(TestCase): +class FormsI18NWidgetsTests(TestCase): def setUp(self): - super(FormsI18NWidgetsTestCase, self).setUp() - activate('de-at') + super(FormsI18NWidgetsTests, self).setUp() + translation.activate('de-at') def tearDown(self): - deactivate() - super(FormsI18NWidgetsTestCase, self).tearDown() + translation.deactivate() + super(FormsI18NWidgetsTests, self).tearDown() def test_datetimeinput(self): w = DateTimeInput() @@ -1056,14 +1184,20 @@ class FormsI18NWidgetsTestCase(TestCase): 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 override('es'): + 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" />') + 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): """ @@ -1072,7 +1206,16 @@ class FormsI18NWidgetsTestCase(TestCase): Refs #17190 """ f = NullBooleanSelectLazyForm() - self.assertHTMLEqual(f.fields['bool'].widget.render('id_bool', True), '<select name="id_bool">\n<option value="1">Unbekannt</option>\n<option value="2" selected="selected">Ja</option>\n<option value="3">Nein</option>\n</select>') + 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): @@ -1151,8 +1294,15 @@ class ClearableFileInputTests(TestCase): """ 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" />') + 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): """ @@ -1185,8 +1335,13 @@ class ClearableFileInputTests(TestCase): """ 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" />') + 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): """ @@ -1238,3 +1393,567 @@ class ClearableFileInputTests(TestCase): '<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(TestCase): + + # 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> + """, + ) + + # 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()) diff --git a/tests/utils_tests/test_encoding.py b/tests/utils_tests/test_encoding.py index 3119b6467a..be2670854f 100644 --- a/tests/utils_tests/test_encoding.py +++ b/tests/utils_tests/test_encoding.py @@ -7,7 +7,7 @@ import datetime from django.utils import six from django.utils.encoding import ( filepath_to_uri, force_bytes, force_text, escape_uri_path, - iri_to_uri, uri_to_iri, + iri_to_uri, uri_to_iri, smart_text, ) from django.utils.http import urlquote_plus @@ -42,13 +42,33 @@ class TestEncodingUtils(unittest.TestCase): today = datetime.date.today() self.assertEqual(force_bytes(today, strings_only=True), today) - def test_escape_uri_path(self): - self.assertEqual( - escape_uri_path('/;some/=awful/?path/:with/@lots/&of/+awful/chars'), - '/%3Bsome/%3Dawful/%3Fpath/:with/@lots/&of/+awful/chars' - ) - self.assertEqual(escape_uri_path('/foo#bar'), '/foo%23bar') - self.assertEqual(escape_uri_path('/foo?bar'), '/foo%3Fbar') + def test_smart_text(self): + class Test: + if six.PY3: + def __str__(self): + return 'ŠĐĆŽćžšđ' + else: + def __str__(self): + return 'ŠĐĆŽćžšđ'.encode('utf-8') + + class TestU: + if six.PY3: + def __str__(self): + return 'ŠĐĆŽćžšđ' + + def __bytes__(self): + return b'Foo' + else: + def __str__(self): + return b'Foo' + + def __unicode__(self): + return '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' + + self.assertEqual(smart_text(Test()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') + self.assertEqual(smart_text(TestU()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') + self.assertEqual(smart_text(1), '1') + self.assertEqual(smart_text('foo'), 'foo') class TestRFC3987IEncodingUtils(unittest.TestCase): @@ -114,3 +134,11 @@ class TestRFC3987IEncodingUtils(unittest.TestCase): for uri, iri in cases: self.assertEqual(iri_to_uri(uri_to_iri(uri)), uri) self.assertEqual(uri_to_iri(iri_to_uri(iri)), iri) + + def test_escape_uri_path(self): + self.assertEqual( + escape_uri_path('/;some/=awful/?path/:with/@lots/&of/+awful/chars'), + '/%3Bsome/%3Dawful/%3Fpath/:with/@lots/&of/+awful/chars' + ) + self.assertEqual(escape_uri_path('/foo#bar'), '/foo%23bar') + self.assertEqual(escape_uri_path('/foo?bar'), '/foo%3Fbar')