From a2e30a41dd00baaa85f0162934327ff0e3dfce51 Mon Sep 17 00:00:00 2001 From: Paul McMillan Date: Mon, 9 Aug 2010 22:11:28 +0000 Subject: [soc2010/test-refactor] Merged back up to trunk git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/test-refactor@13566 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- tests/modeltests/fixtures/tests.py | 51 +- tests/modeltests/model_forms/models.py | 14 +- tests/regressiontests/admin_views/models.py | 6 + tests/regressiontests/admin_views/tests.py | 29 +- tests/regressiontests/forms/forms.py | 40 +- tests/regressiontests/forms/input_formats.py | 894 +++++++++++++++++++++ tests/regressiontests/forms/localflavor/au.py | 2 +- tests/regressiontests/forms/tests.py | 2 + tests/regressiontests/forms/widgets.py | 33 +- tests/regressiontests/localflavor/forms.py | 7 - tests/regressiontests/localflavor/models.py | 8 - tests/regressiontests/localflavor/tests.py | 84 +- tests/regressiontests/localflavor/us/__init__.py | 0 tests/regressiontests/localflavor/us/forms.py | 7 + tests/regressiontests/localflavor/us/models.py | 13 + tests/regressiontests/localflavor/us/tests.py | 82 ++ tests/regressiontests/model_forms_regress/tests.py | 2 +- tests/regressiontests/multiple_database/tests.py | 112 +++ .../regressiontests/test_client_regress/models.py | 26 + .../urlpatterns_reverse/included_namespace_urls.py | 7 + .../urlpatterns_reverse/namespace_urls.py | 9 + tests/regressiontests/urlpatterns_reverse/tests.py | 73 +- tests/regressiontests/urlpatterns_reverse/views.py | 6 + tests/runtests.py | 6 +- 24 files changed, 1359 insertions(+), 154 deletions(-) create mode 100644 tests/regressiontests/forms/input_formats.py delete mode 100644 tests/regressiontests/localflavor/forms.py create mode 100644 tests/regressiontests/localflavor/us/__init__.py create mode 100644 tests/regressiontests/localflavor/us/forms.py create mode 100644 tests/regressiontests/localflavor/us/models.py create mode 100644 tests/regressiontests/localflavor/us/tests.py (limited to 'tests') diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py index 38d59f5a1c..155a5f6ba6 100644 --- a/tests/modeltests/fixtures/tests.py +++ b/tests/modeltests/fixtures/tests.py @@ -21,9 +21,14 @@ class TestCaseFixtureLoadingTests(TestCase): ]) class FixtureLoadingTests(TestCase): - def _dumpdata_assert(self, args, output, format='json', natural_keys=False): + def _dumpdata_assert(self, args, output, format='json', natural_keys=False, + exclude_list=[]): new_io = StringIO.StringIO() - management.call_command('dumpdata', *args, **{'format':format, 'stdout':new_io, 'use_natural_keys':natural_keys}) + management.call_command('dumpdata', *args, **{'format':format, + 'stdout':new_io, + 'stderr':new_io, + 'use_natural_keys':natural_keys, + 'exclude': exclude_list}) command_output = new_io.getvalue().strip() self.assertEqual(command_output, output) @@ -148,6 +153,48 @@ class FixtureLoadingTests(TestCase): self._dumpdata_assert(['fixtures'], """ News StoriesLatest news storiesXML identified as leading cause of cancer2006-06-16 16:00:00Django conquers world!2006-06-16 15:00:00Copyright is fine the way it is2006-06-16 14:00:00Poker on TV is great!2006-06-16 11:00:00Python program becomes self aware2006-06-16 11:00:00copyrightfixturesarticle3legalfixturesarticle3djangofixturesarticle4world dominationfixturesarticle4Artist formerly known as "Prince"Django ReinhardtStephane GrappelliDjango Reinhardtadd_userauthuserchange_userauthuserdelete_userauthuserStephane Grappelliadd_userauthuserdelete_userauthuserArtist formerly known as "Prince"change_userauthuserMusic for all agesArtist formerly known as "Prince"Django Reinhardt""", format='xml', natural_keys=True) + def test_dumpdata_with_excludes(self): + # Load fixture1 which has a site, two articles, and a category + management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + + # Excluding fixtures app should only leave sites + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]', + exclude_list=['fixtures']) + + # Excluding fixtures.Article should leave fixtures.Category + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]', + exclude_list=['fixtures.Article']) + + # Excluding fixtures and fixtures.Article should be a no-op + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]', + exclude_list=['fixtures.Article']) + + # Excluding sites and fixtures.Article should only leave fixtures.Category + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]', + exclude_list=['fixtures.Article', 'sites']) + + # Excluding a bogus app should throw an error + self.assertRaises(SystemExit, + self._dumpdata_assert, + ['fixtures', 'sites'], + '', + exclude_list=['foo_app']) + + # Excluding a bogus model should throw an error + self.assertRaises(SystemExit, + self._dumpdata_assert, + ['fixtures', 'sites'], + '', + exclude_list=['fixtures.FooModel']) + def test_compress_format_loading(self): # Load fixture 4 (compressed), using format specification management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False) diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 1087cf8795..7ded82bb7c 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -554,7 +554,7 @@ fields with the 'choices' attribute are represented by a ChoiceField. -
Hold down "Control", or "Command" on a Mac, to select more than one. +
Hold down "Control", or "Command" on a Mac, to select more than one. You can restrict a form to a subset of the complete list of fields by providing a 'fields' argument. If you try to save a @@ -579,7 +579,7 @@ inserted as 'initial' data in each Field. ... model = Writer >>> f = RoykoForm(auto_id=False, instance=w) >>> print f -Name:
Use both first and last names. +Name:
Use both first and last names. >>> art = Article(headline='Test article', slug='test-article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.') >>> art.save() @@ -609,7 +609,7 @@ inserted as 'initial' data in each Field. - Hold down "Control", or "Command" on a Mac, to select more than one. + Hold down "Control", or "Command" on a Mac, to select more than one. >>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': unicode(w_royko.pk), 'article': 'Hello.'}, instance=art) >>> f.errors {} @@ -672,7 +672,7 @@ Add some categories and test the many-to-many form output. - Hold down "Control", or "Command" on a Mac, to select more than one. + Hold down "Control", or "Command" on a Mac, to select more than one. Initial values can be provided for model forms >>> f = TestArticleForm(auto_id=False, initial={'headline': 'Your headline here', 'categories': ['1','2']}) @@ -696,7 +696,7 @@ Initial values can be provided for model forms - Hold down "Control", or "Command" on a Mac, to select more than one. + Hold down "Control", or "Command" on a Mac, to select more than one. >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04', ... 'writer': unicode(w_royko.pk), 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art) @@ -812,7 +812,7 @@ the data in the database when the form is instantiated. - Hold down "Control", or "Command" on a Mac, to select more than one. + Hold down "Control", or "Command" on a Mac, to select more than one. >>> Category.objects.create(name='Fourth', url='4th') >>> Writer.objects.create(name='Carl Bernstein') @@ -839,7 +839,7 @@ the data in the database when the form is instantiated. - Hold down "Control", or "Command" on a Mac, to select more than one. + Hold down "Control", or "Command" on a Mac, to select more than one. # ModelChoiceField ############################################################ diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index a2700ba747..b25a9b9a96 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -10,6 +10,7 @@ from django.core.mail import EmailMessage from django.db import models from django import forms from django.forms.models import BaseModelFormSet +from django.contrib.auth.models import User from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType @@ -579,6 +580,10 @@ class Pizza(models.Model): class PizzaAdmin(admin.ModelAdmin): readonly_fields = ('toppings',) +class Album(models.Model): + owner = models.ForeignKey(User) + title = models.CharField(max_length=30) + admin.site.register(Article, ArticleAdmin) admin.site.register(CustomArticle, CustomArticleAdmin) admin.site.register(Section, save_as=True, inlines=[ArticleInline]) @@ -625,3 +630,4 @@ admin.site.register(Promo) admin.site.register(ChapterXtra1) admin.site.register(Pizza, PizzaAdmin) admin.site.register(Topping) +admin.site.register(Album) diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 1385e5e0aa..41aade0561 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -2113,11 +2113,9 @@ class ReadonlyTest(TestCase): response = self.client.get('/test_admin/admin/admin_views/pizza/add/') self.assertEqual(response.status_code, 200) -class IncompleteFormTest(TestCase): +class UserAdminTest(TestCase): """ - Tests validation of a ModelForm that doesn't explicitly have all data - corresponding to model fields. Model validation shouldn't fail - such a forms. + Tests user CRUD functionality. """ fixtures = ['admin-views-users.xml'] @@ -2128,6 +2126,7 @@ class IncompleteFormTest(TestCase): self.client.logout() def test_user_creation(self): + user_count = User.objects.count() response = self.client.post('/test_admin/admin/auth/user/add/', { 'username': 'newuser', 'password1': 'newpassword', @@ -2136,6 +2135,7 @@ class IncompleteFormTest(TestCase): }) new_user = User.objects.order_by('-id')[0] self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk) + self.assertEquals(User.objects.count(), user_count + 1) self.assertNotEquals(new_user.password, UNUSABLE_PASSWORD) def test_password_mismatch(self): @@ -2149,3 +2149,24 @@ class IncompleteFormTest(TestCase): self.assert_('password' not in adminform.form.errors) self.assertEquals(adminform.form.errors['password2'], [u"The two password fields didn't match."]) + + def test_user_fk_popup(self): + response = self.client.get('/test_admin/admin/admin_views/album/add/') + self.failUnlessEqual(response.status_code, 200) + self.assertContains(response, '/test_admin/admin/auth/user/add') + self.assertContains(response, 'class="add-another" id="add_id_owner" onclick="return showAddAnotherPopup(this);"') + response = self.client.get('/test_admin/admin/auth/user/add/?_popup=1') + self.assertNotContains(response, 'name="_continue"') + + def test_user_add_another(self): + user_count = User.objects.count() + response = self.client.post('/test_admin/admin/auth/user/add/', { + 'username': 'newuser', + 'password1': 'newpassword', + 'password2': 'newpassword', + '_addanother': '1', + }) + new_user = User.objects.order_by('-id')[0] + self.assertRedirects(response, '/test_admin/admin/auth/user/add/') + self.assertEquals(User.objects.count(), user_count + 1) + self.assertNotEquals(new_user.password, UNUSABLE_PASSWORD) diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py index 58051fd133..9e45a267db 100644 --- a/tests/regressiontests/forms/forms.py +++ b/tests/regressiontests/forms/forms.py @@ -705,13 +705,13 @@ Form.clean() is required to return a dictionary of all clean data. >>> print f.as_table() Username: -Password1: -Password2: +Password1: +Password2: >>> print f.as_ul()
    • Please make sure your passwords match.
  • Username:
  • -
  • Password1:
  • -
  • Password2:
  • +
  • Password1:
  • +
  • Password2:
  • >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} @@ -1258,20 +1258,20 @@ to a Field class. This help text is displayed when a Form is rendered. ... password = CharField(widget=PasswordInput, help_text='Choose wisely.') >>> p = UserRegistration(auto_id=False) >>> print p.as_ul() -
  • Username: e.g., user@example.com
  • -
  • Password: Choose wisely.
  • +
  • Username: e.g., user@example.com
  • +
  • Password: Choose wisely.
  • >>> print p.as_p() -

    Username: e.g., user@example.com

    -

    Password: Choose wisely.

    +

    Username: e.g., user@example.com

    +

    Password: Choose wisely.

    >>> print p.as_table() -Username:
    e.g., user@example.com -Password:
    Choose wisely. +Username:
    e.g., user@example.com +Password:
    Choose wisely. The help text is displayed whether or not data is provided for the form. >>> p = UserRegistration({'username': u'foo'}, auto_id=False) >>> print p.as_ul() -
  • Username: e.g., user@example.com
  • -
    • This field is required.
    Password: Choose wisely.
  • +
  • Username: e.g., user@example.com
  • +
    • This field is required.
    Password: Choose wisely.
  • help_text is not displayed for hidden fields. It can be used for documentation purposes, though. @@ -1281,7 +1281,7 @@ purposes, though. ... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination') >>> p = UserRegistration(auto_id=False) >>> print p.as_ul() -
  • Username: e.g., user@example.com
  • +
  • Username: e.g., user@example.com
  • Password:
  • Help text can include arbitrary Unicode characters. @@ -1289,7 +1289,7 @@ Help text can include arbitrary Unicode characters. ... username = CharField(max_length=10, help_text='ŠĐĆŽćžšđ') >>> p = UserRegistration(auto_id=False) >>> p.as_ul() -u'
  • Username: \u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111
  • ' +u'
  • Username: \u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111
  • ' # Subclassing forms ########################################################### @@ -1589,8 +1589,8 @@ Case 2: POST with erroneous data (a redisplayed form, with errors). - - + +
    • Please make sure your passwords match.
    Username:
    • Ensure this value has at most 10 characters (it has 23).
    Password1:
    Password2:
    Password1:
    Password2:
    @@ -1719,8 +1719,8 @@ the list of errors is empty). You can also use it in {% if %} statements. >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))

    -

    -

    +

    +

    >>> t = Template('''
    @@ -1734,8 +1734,8 @@ the list of errors is empty). You can also use it in {% if %} statements.
    • Please make sure your passwords match.

    -

    -

    +

    +

    diff --git a/tests/regressiontests/forms/input_formats.py b/tests/regressiontests/forms/input_formats.py new file mode 100644 index 0000000000..498c6de9fb --- /dev/null +++ b/tests/regressiontests/forms/input_formats.py @@ -0,0 +1,894 @@ +from datetime import time, date, datetime +from unittest import TestCase + +from django import forms +from django.conf import settings +from django.utils.translation import activate, deactivate + + +class LocalizedTimeTests(TestCase): + def setUp(self): + self.old_TIME_INPUT_FORMATS = settings.TIME_INPUT_FORMATS + self.old_USE_L10N = settings.USE_L10N + + settings.TIME_INPUT_FORMATS = ["%I:%M:%S %p", "%I:%M %p"] + settings.USE_L10N = True + + activate('de') + + def tearDown(self): + settings.TIME_INPUT_FORMATS = self.old_TIME_INPUT_FORMATS + settings.USE_L10N = self.old_USE_L10N + + deactivate() + + def test_timeField(self): + "TimeFields can parse dates in the default format" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '13:30:05') + + # Parse a time in a valid, but non-default format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField(self): + "Localized TimeFields act as unlocalized widgets" + f = forms.TimeField(localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_timeField_with_inputformat(self): + "TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"]) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField_with_inputformat(self): + "Localized TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + +class CustomTimeInputFormatsTests(TestCase): + def setUp(self): + self.old_TIME_INPUT_FORMATS = settings.TIME_INPUT_FORMATS + settings.TIME_INPUT_FORMATS = ["%I:%M:%S %p", "%I:%M %p"] + + def tearDown(self): + settings.TIME_INPUT_FORMATS = self.old_TIME_INPUT_FORMATS + + def test_timeField(self): + "TimeFields can parse dates in the default format" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM') + + # Parse a time in a valid, but non-default format, get a parsed result + result = f.clean('1:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + def test_localized_timeField(self): + "Localized TimeFields act as unlocalized widgets" + f = forms.TimeField(localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('01:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + def test_timeField_with_inputformat(self): + "TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"]) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + def test_localized_timeField_with_inputformat(self): + "Localized TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + +class SimpleTimeFormatTests(TestCase): + def test_timeField(self): + "TimeFields can parse dates in the default format" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid, but non-default format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField(self): + "Localized TimeFields in a non-localized environment act as unlocalized widgets" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_timeField_with_inputformat(self): + "TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"]) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField_with_inputformat(self): + "Localized TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"], localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + +class LocalizedDateTests(TestCase): + def setUp(self): + self.old_DATE_INPUT_FORMATS = settings.DATE_INPUT_FORMATS + self.old_USE_L10N = settings.USE_L10N + + settings.DATE_INPUT_FORMATS = ["%d/%m/%Y", "%d-%m-%Y"] + settings.USE_L10N = True + + activate('de') + + def tearDown(self): + settings.DATE_INPUT_FORMATS = self.old_DATE_INPUT_FORMATS + settings.USE_L10N = self.old_USE_L10N + + deactivate() + + def test_dateField(self): + "DateFields can parse dates in the default format" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('21.12.10') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField(self): + "Localized DateFields act as unlocalized widgets" + f = forms.DateField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.10') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_dateField_with_inputformat(self): + "DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField_with_inputformat(self): + "Localized DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + +class CustomDateInputFormatsTests(TestCase): + def setUp(self): + self.old_DATE_INPUT_FORMATS = settings.DATE_INPUT_FORMATS + settings.DATE_INPUT_FORMATS = ["%d.%m.%Y", "%d-%m-%Y"] + + def tearDown(self): + settings.DATE_INPUT_FORMATS = self.old_DATE_INPUT_FORMATS + + def test_dateField(self): + "DateFields can parse dates in the default format" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField(self): + "Localized DateFields act as unlocalized widgets" + f = forms.DateField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_dateField_with_inputformat(self): + "DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField_with_inputformat(self): + "Localized DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + +class SimpleDateFormatTests(TestCase): + def test_dateField(self): + "DateFields can parse dates in the default format" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('12/21/2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + def test_localized_dateField(self): + "Localized DateFields in a non-localized environment act as unlocalized widgets" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12/21/2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + def test_dateField_with_inputformat(self): + "DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + def test_localized_dateField_with_inputformat(self): + "Localized DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + +class LocalizedDateTimeTests(TestCase): + def setUp(self): + self.old_DATETIME_INPUT_FORMATS = settings.DATETIME_INPUT_FORMATS + self.old_USE_L10N = settings.USE_L10N + + settings.DATETIME_INPUT_FORMATS = ["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"] + settings.USE_L10N = True + + activate('de') + + def tearDown(self): + settings.DATETIME_INPUT_FORMATS = self.old_DATETIME_INPUT_FORMATS + settings.USE_L10N = self.old_USE_L10N + + deactivate() + + def test_dateTimeField(self): + "DateTimeFields can parse dates in the default format" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010 13:30:05') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('21.12.2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + def test_localized_dateTimeField(self): + "Localized DateTimeFields act as unlocalized widgets" + f = forms.DateTimeField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + def test_dateTimeField_with_inputformat(self): + "DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05 13:30:05') + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30.05 12.21.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30 12-21-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + def test_localized_dateTimeField_with_inputformat(self): + "Localized DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30.05 12.21.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30 12-21-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + +class CustomDateTimeInputFormatsTests(TestCase): + def setUp(self): + self.old_DATETIME_INPUT_FORMATS = settings.DATETIME_INPUT_FORMATS + settings.DATETIME_INPUT_FORMATS = ["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"] + + def tearDown(self): + settings.DATETIME_INPUT_FORMATS = self.old_DATETIME_INPUT_FORMATS + + def test_dateTimeField(self): + "DateTimeFields can parse dates in the default format" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21/12/2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM 21/12/2010') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + + def test_localized_dateTimeField(self): + "Localized DateTimeFields act as unlocalized widgets" + f = forms.DateTimeField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21/12/2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM 21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + + def test_dateTimeField_with_inputformat(self): + "DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM 21/12/2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + + def test_localized_dateTimeField_with_inputformat(self): + "Localized DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM 21/12/2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + +class SimpleDateTimeFormatTests(TestCase): + def test_dateTimeField(self): + "DateTimeFields can parse dates in the default format" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('12/21/2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + def test_localized_dateTimeField(self): + "Localized DateTimeFields in a non-localized environment act as unlocalized widgets" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12/21/2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + def test_dateTimeField_with_inputformat(self): + "DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21.12.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:00") + + def test_localized_dateTimeField_with_inputformat(self): + "Localized DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21.12.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:00") diff --git a/tests/regressiontests/forms/localflavor/au.py b/tests/regressiontests/forms/localflavor/au.py index fd4c0d6980..cda782094a 100644 --- a/tests/regressiontests/forms/localflavor/au.py +++ b/tests/regressiontests/forms/localflavor/au.py @@ -50,7 +50,7 @@ u'' ## AUPhoneNumberField ######################################################## A field that accepts a 10 digit Australian phone number. -llows spaces and parentheses around area code. +Allows spaces and parentheses around area code. >>> from django.contrib.localflavor.au.forms import AUPhoneNumberField >>> f = AUPhoneNumberField() diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 8757e799a9..7a91cb701e 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -41,6 +41,8 @@ from fields import FieldsTests from validators import TestFieldWithValidators from widgets import WidgetTests +from input_formats import * + __test__ = { 'extra_tests': extra_tests, 'form_tests': form_tests, diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py index 39d7d569a3..b8ec789649 100644 --- a/tests/regressiontests/forms/widgets.py +++ b/tests/regressiontests/forms/widgets.py @@ -62,6 +62,17 @@ u'' u'' >>> w.render('email', None) u'' +>>> w.render('email', 'secret') +u'' + +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) +>>> w.render('email', '') +u'' +>>> w.render('email', None) +u'' >>> w.render('email', 'test@example.com') u'' >>> w.render('email', 'some "quoted" & ampersanded value') @@ -70,36 +81,20 @@ u'' You can also pass 'attrs' to the constructor: ->>> w = PasswordInput(attrs={'class': 'fun'}) +>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=True) >>> w.render('email', '') u'' >>> w.render('email', 'foo@example.com') u'' 'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = PasswordInput(attrs={'class': 'pretty'}) +>>> w = PasswordInput(attrs={'class': 'pretty'}, render_value=True) >>> w.render('email', '', attrs={'class': 'special'}) u'' >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) u'' -The render_value argument lets you specify whether the widget should render -its value. You may want to do this for security reasons. ->>> w = PasswordInput(render_value=True) ->>> w.render('email', 'secret') -u'' ->>> w = PasswordInput(render_value=False) ->>> w.render('email', '') -u'' ->>> w.render('email', None) -u'' ->>> w.render('email', 'secret') -u'' ->>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False) ->>> w.render('email', 'secret') -u'' - # HiddenInput Widget ############################################################ >>> w = HiddenInput() @@ -1286,7 +1281,7 @@ class SelectAndTextWidget(forms.MultiWidget): forms.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 diff --git a/tests/regressiontests/localflavor/forms.py b/tests/regressiontests/localflavor/forms.py deleted file mode 100644 index 2dd1da6dd0..0000000000 --- a/tests/regressiontests/localflavor/forms.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.forms import ModelForm -from models import Place - -class PlaceForm(ModelForm): - """docstring for PlaceForm""" - class Meta: - model = Place diff --git a/tests/regressiontests/localflavor/models.py b/tests/regressiontests/localflavor/models.py index f74a5051d4..e69de29bb2 100644 --- a/tests/regressiontests/localflavor/models.py +++ b/tests/regressiontests/localflavor/models.py @@ -1,8 +0,0 @@ -from django.db import models -from django.contrib.localflavor.us.models import USStateField - -class Place(models.Model): - state = USStateField(blank=True) - state_req = USStateField() - state_default = USStateField(default="CA", blank=True) - name = models.CharField(max_length=20) diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py index 0ea3c52568..69682360d3 100644 --- a/tests/regressiontests/localflavor/tests.py +++ b/tests/regressiontests/localflavor/tests.py @@ -1,83 +1,5 @@ +import unittest from django.test import TestCase -from models import Place -from forms import PlaceForm -class USLocalflavorTests(TestCase): - def setUp(self): - self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'}) - - def test_get_display_methods(self): - """Test that the get_*_display() methods are added to the model instances.""" - place = self.form.save() - self.assertEqual(place.get_state_display(), 'Georgia') - self.assertEqual(place.get_state_req_display(), 'North Carolina') - - def test_required(self): - """Test that required USStateFields throw appropriate errors.""" - form = PlaceForm({'state':'GA', 'name':'Place in GA'}) - self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['state_req'], [u'This field is required.']) - - def test_field_blank_option(self): - """Test that the empty option is there.""" - state_select_html = """\ -""" - self.assertEqual(str(self.form['state']), state_select_html) +# just import your tests here +from us.tests import * diff --git a/tests/regressiontests/localflavor/us/__init__.py b/tests/regressiontests/localflavor/us/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/localflavor/us/forms.py b/tests/regressiontests/localflavor/us/forms.py new file mode 100644 index 0000000000..9b77e1078b --- /dev/null +++ b/tests/regressiontests/localflavor/us/forms.py @@ -0,0 +1,7 @@ +from django.forms import ModelForm +from models import USPlace + +class USPlaceForm(ModelForm): + """docstring for PlaceForm""" + class Meta: + model = USPlace diff --git a/tests/regressiontests/localflavor/us/models.py b/tests/regressiontests/localflavor/us/models.py new file mode 100644 index 0000000000..a8a4cf0f50 --- /dev/null +++ b/tests/regressiontests/localflavor/us/models.py @@ -0,0 +1,13 @@ +from django.db import models +from django.contrib.localflavor.us.models import USStateField + +# When creating models you need to remember to add a app_label as +# 'localflavor', so your model can be found + +class USPlace(models.Model): + state = USStateField(blank=True) + state_req = USStateField() + state_default = USStateField(default="CA", blank=True) + name = models.CharField(max_length=20) + class Meta: + app_label = 'localflavor' diff --git a/tests/regressiontests/localflavor/us/tests.py b/tests/regressiontests/localflavor/us/tests.py new file mode 100644 index 0000000000..07fe057833 --- /dev/null +++ b/tests/regressiontests/localflavor/us/tests.py @@ -0,0 +1,82 @@ +from django.test import TestCase +from forms import USPlaceForm + +class USLocalflavorTests(TestCase): + def setUp(self): + self.form = USPlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'}) + + def test_get_display_methods(self): + """Test that the get_*_display() methods are added to the model instances.""" + place = self.form.save() + self.assertEqual(place.get_state_display(), 'Georgia') + self.assertEqual(place.get_state_req_display(), 'North Carolina') + + def test_required(self): + """Test that required USStateFields throw appropriate errors.""" + form = USPlaceForm({'state':'GA', 'name':'Place in GA'}) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors['state_req'], [u'This field is required.']) + + def test_field_blank_option(self): + """Test that the empty option is there.""" + state_select_html = """\ +""" + self.assertEqual(str(self.form['state']), state_select_html) diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py index 2e77a88977..5bee73ce7e 100644 --- a/tests/regressiontests/model_forms_regress/tests.py +++ b/tests/regressiontests/model_forms_regress/tests.py @@ -129,7 +129,7 @@ class ManyToManyCallableInitialTests(TestCase): - Hold down "Control", or "Command" on a Mac, to select more than one.""") + Hold down "Control", or "Command" on a Mac, to select more than one.""") class CFFForm(forms.ModelForm): class Meta: diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 6675fdcc6c..b55e708944 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -7,6 +7,7 @@ from django.conf import settings from django.contrib.auth.models import User from django.core import management from django.db import connections, router, DEFAULT_DB_ALIAS +from django.db.models import signals from django.db.utils import ConnectionRouter from django.test import TestCase @@ -1619,3 +1620,114 @@ class PickleQuerySetTestCase(TestCase): Book.objects.using(db).create(title='Dive into Python', published=datetime.date(2009, 5, 4)) qs = Book.objects.all() self.assertEqual(qs.db, pickle.loads(pickle.dumps(qs)).db) + + +class DatabaseReceiver(object): + """ + Used in the tests for the database argument in signals (#13552) + """ + def __call__(self, signal, sender, **kwargs): + self._database = kwargs['using'] + +class WriteToOtherRouter(object): + """ + A router that sends all writes to the other database. + """ + def db_for_write(self, model, **hints): + return "other" + +class SignalTests(TestCase): + multi_db = True + + def setUp(self): + self.old_routers = router.routers + + def tearDown(self): + router.routser = self.old_routers + + def _write_to_other(self): + "Sends all writes to 'other'." + router.routers = [WriteToOtherRouter()] + + def _write_to_default(self): + "Sends all writes to the default DB" + router.routers = self.old_routers + + def test_database_arg_save_and_delete(self): + """ + Tests that the pre/post_save signal contains the correct database. + (#13552) + """ + # Make some signal receivers + pre_save_receiver = DatabaseReceiver() + post_save_receiver = DatabaseReceiver() + pre_delete_receiver = DatabaseReceiver() + post_delete_receiver = DatabaseReceiver() + # Make model and connect receivers + signals.pre_save.connect(sender=Person, receiver=pre_save_receiver) + signals.post_save.connect(sender=Person, receiver=post_save_receiver) + signals.pre_delete.connect(sender=Person, receiver=pre_delete_receiver) + signals.post_delete.connect(sender=Person, receiver=post_delete_receiver) + p = Person.objects.create(name='Darth Vader') + # Save and test receivers got calls + p.save() + self.assertEqual(pre_save_receiver._database, DEFAULT_DB_ALIAS) + self.assertEqual(post_save_receiver._database, DEFAULT_DB_ALIAS) + # Delete, and test + p.delete() + self.assertEqual(pre_delete_receiver._database, DEFAULT_DB_ALIAS) + self.assertEqual(post_delete_receiver._database, DEFAULT_DB_ALIAS) + # Save again to a different database + p.save(using="other") + self.assertEqual(pre_save_receiver._database, "other") + self.assertEqual(post_save_receiver._database, "other") + # Delete, and test + p.delete(using="other") + self.assertEqual(pre_delete_receiver._database, "other") + self.assertEqual(post_delete_receiver._database, "other") + + def test_database_arg_m2m(self): + """ + Test that the m2m_changed signal has a correct database arg (#13552) + """ + # Make a receiver + receiver = DatabaseReceiver() + # Connect it, and make the models + signals.m2m_changed.connect(receiver=receiver) + + b = Book.objects.create(title="Pro Django", + published=datetime.date(2008, 12, 16)) + + p = Person.objects.create(name="Marty Alchin") + + # Test addition + b.authors.add(p) + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + b.authors.add(p) + self._write_to_default() + self.assertEqual(receiver._database, "other") + + # Test removal + b.authors.remove(p) + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + b.authors.remove(p) + self._write_to_default() + self.assertEqual(receiver._database, "other") + + # Test addition in reverse + p.book_set.add(b) + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + p.book_set.add(b) + self._write_to_default() + self.assertEqual(receiver._database, "other") + + # Test clearing + b.authors.clear() + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + b.authors.clear() + self._write_to_default() + self.assertEqual(receiver._database, "other") diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index 84668f3983..dc469bafa2 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse from django.core.exceptions import SuspiciousOperation from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context from django.template import loader +from django.test.client import encode_file class AssertContainsTests(TestCase): def setUp(self): @@ -619,6 +620,7 @@ class ContextTests(TestCase): "Context variables can be retrieved from a single context" response = self.client.get("/test_client_regress/request_data/", data={'foo':'whiz'}) self.assertEqual(response.context.__class__, Context) + self.assertTrue('get-foo' in response.context) self.assertEqual(response.context['get-foo'], 'whiz') self.assertEqual(response.context['request-foo'], 'whiz') self.assertEqual(response.context['data'], 'sausage') @@ -634,6 +636,7 @@ class ContextTests(TestCase): response = self.client.get("/test_client_regress/request_data_extended/", data={'foo':'whiz'}) self.assertEqual(response.context.__class__, ContextList) self.assertEqual(len(response.context), 2) + self.assertTrue('get-foo' in response.context) self.assertEqual(response.context['get-foo'], 'whiz') self.assertEqual(response.context['request-foo'], 'whiz') self.assertEqual(response.context['data'], 'bacon') @@ -821,3 +824,26 @@ class UnicodePayloadTests(TestCase): response = self.client.post("/test_client_regress/parse_unicode_json/", json, content_type="application/json; charset=koi8-r") self.assertEqual(response.content, json.encode('koi8-r')) + +class DummyFile(object): + def __init__(self, filename): + self.name = filename + def read(self): + return 'TEST_FILE_CONTENT' + +class UploadedFileEncodingTest(TestCase): + def test_file_encoding(self): + encoded_file = encode_file('TEST_BOUNDARY', 'TEST_KEY', DummyFile('test_name.bin')) + self.assertEqual('--TEST_BOUNDARY', encoded_file[0]) + self.assertEqual('Content-Disposition: form-data; name="TEST_KEY"; filename="test_name.bin"', encoded_file[1]) + self.assertEqual('TEST_FILE_CONTENT', encoded_file[-1]) + + def test_guesses_content_type_on_file_encoding(self): + self.assertEqual('Content-Type: application/octet-stream', + encode_file('IGNORE', 'IGNORE', DummyFile("file.bin"))[2]) + self.assertEqual('Content-Type: text/plain', + encode_file('IGNORE', 'IGNORE', DummyFile("file.txt"))[2]) + self.assertEqual('Content-Type: application/zip', + encode_file('IGNORE', 'IGNORE', DummyFile("file.zip"))[2]) + self.assertEqual('Content-Type: application/octet-stream', + encode_file('IGNORE', 'IGNORE', DummyFile("file.unknown"))[2]) diff --git a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py index 073190657c..16887e2a9b 100644 --- a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py +++ b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py @@ -1,5 +1,6 @@ from django.conf.urls.defaults import * from namespace_urls import URLObject +from views import view_class_instance testobj3 = URLObject('testapp', 'test-ns3') @@ -7,7 +8,13 @@ urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', url(r'^normal/$', 'empty_view', name='inc-normal-view'), url(r'^normal/(?P\d+)/(?P\d+)/$', 'empty_view', name='inc-normal-view'), + url(r'^mixed_args/(\d+)/(?P\d+)/$', 'empty_view', name='inc-mixed-args'), + url(r'^no_kwargs/(\d+)/(\d+)/$', 'empty_view', name='inc-no-kwargs'), + + url(r'^view_class/(?P\d+)/(?P\d+)/$', view_class_instance, name='inc-view-class'), + (r'^test3/', include(testobj3.urls)), (r'^ns-included3/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns3')), + (r'^ns-included4/', include('regressiontests.urlpatterns_reverse.namespace_urls', namespace='inc-ns4')), ) diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py index 27cc7f7a22..3d34049932 100644 --- a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py +++ b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py @@ -1,4 +1,5 @@ from django.conf.urls.defaults import * +from views import view_class_instance class URLObject(object): def __init__(self, app_name, namespace): @@ -23,6 +24,14 @@ urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', url(r'^normal/$', 'empty_view', name='normal-view'), url(r'^normal/(?P\d+)/(?P\d+)/$', 'empty_view', name='normal-view'), + url(r'^mixed_args/(\d+)/(?P\d+)/$', 'empty_view', name='mixed-args'), + url(r'^no_kwargs/(\d+)/(\d+)/$', 'empty_view', name='no-kwargs'), + + url(r'^view_class/(?P\d+)/(?P\d+)/$', view_class_instance, name='view-class'), + + (r'^unnamed/normal/(?P\d+)/(?P\d+)/$', 'empty_view'), + (r'^unnamed/view_class/(?P\d+)/(?P\d+)/$', view_class_instance), + (r'^test1/', include(testobj1.urls)), (r'^test2/', include(testobj2.urls)), (r'^default/', include(default_testobj.urls)), diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py index 3fcc935da2..dcd942649c 100644 --- a/tests/regressiontests/urlpatterns_reverse/tests.py +++ b/tests/regressiontests/urlpatterns_reverse/tests.py @@ -18,7 +18,7 @@ import unittest from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404 +from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404, ResolverMatch from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.shortcuts import redirect from django.test import TestCase @@ -26,6 +26,41 @@ from django.test import TestCase import urlconf_outer import urlconf_inner import middleware +import views + +resolve_test_data = ( + # These entries are in the format: (path, url_name, app_name, namespace, view_func, args, kwargs) + # Simple case + ('/normal/42/37/', 'normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/view_class/42/37/', 'view-class', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/normal/42/37/', 'inc-normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/view_class/42/37/', 'inc-view-class', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}), + + # Unnamed args are dropped if you have *any* kwargs in a pattern + ('/mixed_args/42/37/', 'mixed-args', None, '', views.empty_view, tuple(), {'arg2': '37'}), + ('/included/mixed_args/42/37/', 'inc-mixed-args', None, '', views.empty_view, tuple(), {'arg2': '37'}), + + # Unnamed views will be resolved to the function/class name + ('/unnamed/normal/42/37/', 'regressiontests.urlpatterns_reverse.views.empty_view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/unnamed/view_class/42/37/', 'regressiontests.urlpatterns_reverse.views.ViewClass', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}), + + # If you have no kwargs, you get an args list. + ('/no_kwargs/42/37/', 'no-kwargs', None, '', views.empty_view, ('42','37'), {}), + ('/included/no_kwargs/42/37/', 'inc-no-kwargs', None, '', views.empty_view, ('42','37'), {}), + + # Namespaces + ('/test1/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/ns-included1/normal/42/37/', 'inc-normal-view', None, 'inc-ns1', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/default/inner/42/37/', 'urlobject-view', 'testapp', 'testapp', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/other2/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns2', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/other1/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + + # Nested namespaces + ('/ns-included1/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/ns-included1/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:inc-ns4:inc-ns2:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), +) test_data = ( ('places', '/places/3/', [3], {}), @@ -119,6 +154,10 @@ class URLPatternReverse(TestCase): else: self.assertEquals(got, expected) + def test_reverse_none(self): + # Reversing None should raise an error, not return the last un-named view. + self.assertRaises(NoReverseMatch, reverse, None) + class ResolverTests(unittest.TestCase): def test_non_regex(self): """ @@ -229,6 +268,12 @@ class NamespaceTests(TestCase): self.assertEquals('/ns-included1/test3/inner/37/42/', reverse('inc-ns1:test-ns3:urlobject-view', args=[37,42])) self.assertEquals('/ns-included1/test3/inner/42/37/', reverse('inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37})) + def test_nested_namespace_pattern(self): + "Namespaces can be nested" + self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view')) + self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/37/42/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', args=[37,42])) + self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/42/37/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37})) + def test_app_lookup_object(self): "A default application namespace can be used for lookup" self.assertEquals('/default/inner/', reverse('testapp:urlobject-view')) @@ -317,3 +362,29 @@ class NoRootUrlConfTests(TestCase): def test_no_handler_exception(self): self.assertRaises(ImproperlyConfigured, self.client.get, '/test/me/') + +class ResolverMatchTests(TestCase): + urls = 'regressiontests.urlpatterns_reverse.namespace_urls' + + def test_urlpattern_resolve(self): + for path, name, app_name, namespace, func, args, kwargs in resolve_test_data: + # Test legacy support for extracting "function, args, kwargs" + match_func, match_args, match_kwargs = resolve(path) + self.assertEqual(match_func, func) + self.assertEqual(match_args, args) + self.assertEqual(match_kwargs, kwargs) + + # Test ResolverMatch capabilities. + match = resolve(path) + self.assertEqual(match.__class__, ResolverMatch) + self.assertEqual(match.url_name, name) + self.assertEqual(match.args, args) + self.assertEqual(match.kwargs, kwargs) + self.assertEqual(match.app_name, app_name) + self.assertEqual(match.namespace, namespace) + self.assertEqual(match.func, func) + + # ... and for legacy purposes: + self.assertEquals(match[0], func) + self.assertEquals(match[1], args) + self.assertEquals(match[2], kwargs) diff --git a/tests/regressiontests/urlpatterns_reverse/views.py b/tests/regressiontests/urlpatterns_reverse/views.py index 99c00bde70..27dca3d0e2 100644 --- a/tests/regressiontests/urlpatterns_reverse/views.py +++ b/tests/regressiontests/urlpatterns_reverse/views.py @@ -6,3 +6,9 @@ def kwargs_view(request, arg1=1, arg2=2): def absolute_kwargs_view(request, arg1=1, arg2=2): pass + +class ViewClass(object): + def __call__(self, request, *args, **kwargs): + pass + +view_class_instance = ViewClass() diff --git a/tests/runtests.py b/tests/runtests.py index 5585f75d5a..4d7c55bae0 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -128,7 +128,7 @@ def django_tests(verbosity, interactive, failfast, test_labels): # no models were named (i.e., run all), import # this model and add it to the list to test. if not test_labels or model_name in set([label.split('.')[0] for label in test_labels]): - if verbosity >= 1: + if verbosity >= 2: print "Importing model %s" % model_name mod = load_app(model_label) if mod: @@ -187,8 +187,8 @@ if __name__ == "__main__": from optparse import OptionParser usage = "%prog [options] [model model model ...]" parser = OptionParser(usage=usage) - parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='0', - type='choice', choices=['0', '1', '2'], + parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='1', + type='choice', choices=['0', '1', '2', '3'], help='Verbosity level; 0=minimal output, 1=normal output, 2=all output') parser.add_option('--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.') -- cgit v1.3