diff options
Diffstat (limited to 'tests/regressiontests')
35 files changed, 1484 insertions, 0 deletions
diff --git a/tests/regressiontests/generic_views/__init__.py b/tests/regressiontests/generic_views/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/generic_views/__init__.py diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py new file mode 100644 index 0000000000..a1da98690b --- /dev/null +++ b/tests/regressiontests/generic_views/base.py @@ -0,0 +1,233 @@ +import unittest + +from django.core.exceptions import ImproperlyConfigured +from django.http import HttpResponse +from django.test import TestCase, RequestFactory +from django.utils import simplejson +from django.views.generic import View, TemplateView, RedirectView + + +class SimpleView(View): + """ + A simple view with a docstring. + """ + def get(self, request): + return HttpResponse('This is a simple view') + + +class SimplePostView(SimpleView): + post = SimpleView.get + + +class CustomizableView(SimpleView): + parameter = {} + +def decorator(view): + view.is_decorated = True + return view + + +class DecoratedDispatchView(SimpleView): + + @decorator + def dispatch(self, request, *args, **kwargs): + return super(DecoratedDispatchView, self).dispatch(request, *args, **kwargs) + + +class AboutTemplateView(TemplateView): + def get(self, request): + return self.render_to_response({}) + + def get_template_names(self): + return ['generic_views/about.html'] + + +class AboutTemplateAttributeView(TemplateView): + template_name = 'generic_views/about.html' + + def get(self, request): + return self.render_to_response(context={}) + + +class InstanceView(View): + + def get(self, request): + return self + + +class ViewTest(unittest.TestCase): + rf = RequestFactory() + + def _assert_simple(self, response): + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, 'This is a simple view') + + def test_no_init_kwargs(self): + """ + Test that a view can't be accidentally instantiated before deployment + """ + try: + view = SimpleView(key='value').as_view() + self.fail('Should not be able to instantiate a view') + except AttributeError: + pass + + def test_no_init_args(self): + """ + Test that a view can't be accidentally instantiated before deployment + """ + try: + view = SimpleView.as_view('value') + self.fail('Should not be able to use non-keyword arguments instantiating a view') + except TypeError: + pass + + def test_pathological_http_method(self): + """ + The edge case of a http request that spoofs an existing method name is caught. + """ + self.assertEqual(SimpleView.as_view()( + self.rf.get('/', REQUEST_METHOD='DISPATCH') + ).status_code, 405) + + def test_get_only(self): + """ + Test a view which only allows GET doesn't allow other methods. + """ + self._assert_simple(SimpleView.as_view()(self.rf.get('/'))) + self.assertEqual(SimpleView.as_view()(self.rf.post('/')).status_code, 405) + self.assertEqual(SimpleView.as_view()( + self.rf.get('/', REQUEST_METHOD='FAKE') + ).status_code, 405) + + def test_get_and_post(self): + """ + Test a view which only allows both GET and POST. + """ + self._assert_simple(SimplePostView.as_view()(self.rf.get('/'))) + self._assert_simple(SimplePostView.as_view()(self.rf.post('/'))) + self.assertEqual(SimplePostView.as_view()( + self.rf.get('/', REQUEST_METHOD='FAKE') + ).status_code, 405) + + def test_invalid_keyword_argument(self): + """ + Test that view arguments must be predefined on the class and can't + be named like a HTTP method. + """ + # Check each of the allowed method names + for method in SimpleView.http_method_names: + kwargs = dict(((method, "value"),)) + self.assertRaises(TypeError, SimpleView.as_view, **kwargs) + + # Check the case view argument is ok if predefined on the class... + CustomizableView.as_view(parameter="value") + # ...but raises errors otherwise. + self.assertRaises(TypeError, CustomizableView.as_view, foobar="value") + + def test_calling_more_than_once(self): + """ + Test a view can only be called once. + """ + request = self.rf.get('/') + view = InstanceView.as_view() + self.assertNotEqual(view(request), view(request)) + + def test_class_attributes(self): + """ + Test that the callable returned from as_view() has proper + docstring, name and module. + """ + self.assertEqual(SimpleView.__doc__, SimpleView.as_view().__doc__) + self.assertEqual(SimpleView.__name__, SimpleView.as_view().__name__) + self.assertEqual(SimpleView.__module__, SimpleView.as_view().__module__) + + def test_dispatch_decoration(self): + """ + Test that attributes set by decorators on the dispatch method + are also present on the closure. + """ + self.assertTrue(DecoratedDispatchView.as_view().is_decorated) + + +class TemplateViewTest(TestCase): + urls = 'regressiontests.generic_views.urls' + + rf = RequestFactory() + + def _assert_about(self, response): + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, '<h1>About</h1>') + + def test_get(self): + """ + Test a view that simply renders a template on GET + """ + self._assert_about(AboutTemplateView.as_view()(self.rf.get('/about/'))) + + def test_get_template_attribute(self): + """ + Test a view that renders a template on GET with the template name as + an attribute on the class. + """ + self._assert_about(AboutTemplateAttributeView.as_view()(self.rf.get('/about/'))) + + def test_get_generic_template(self): + """ + Test a completely generic view that renders a template on GET + with the template name as an argument at instantiation. + """ + self._assert_about(TemplateView.as_view(template_name='generic_views/about.html')(self.rf.get('/about/'))) + + def test_template_params(self): + """ + A generic template view passes kwargs as context. + """ + response = self.client.get('/template/simple/bar/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['params'], {'foo': 'bar'}) + + def test_extra_template_params(self): + """ + A template view can be customized to return extra context. + """ + response = self.client.get('/template/custom/bar/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['params'], {'foo': 'bar'}) + self.assertEqual(response.context['key'], 'value') + +class RedirectViewTest(unittest.TestCase): + rf = RequestFactory() + + def test_no_url(self): + "Without any configuration, returns HTTP 410 GONE" + response = RedirectView.as_view()(self.rf.get('/foo/')) + self.assertEquals(response.status_code, 410) + + def test_permanaent_redirect(self): + "Default is a permanent redirect" + response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) + self.assertEquals(response.status_code, 301) + self.assertEquals(response['Location'], '/bar/') + + def test_temporary_redirect(self): + "Permanent redirects are an option" + response = RedirectView.as_view(url='/bar/', permanent=False)(self.rf.get('/foo/')) + self.assertEquals(response.status_code, 302) + self.assertEquals(response['Location'], '/bar/') + + def test_include_args(self): + "GET arguments can be included in the redirected URL" + response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) + self.assertEquals(response.status_code, 301) + self.assertEquals(response['Location'], '/bar/') + + response = RedirectView.as_view(url='/bar/', query_string=True)(self.rf.get('/foo/?pork=spam')) + self.assertEquals(response.status_code, 301) + self.assertEquals(response['Location'], '/bar/?pork=spam') + + def test_parameter_substitution(self): + "Redirection URLs can be parameterized" + response = RedirectView.as_view(url='/bar/%(object_id)d/')(self.rf.get('/foo/42/'), object_id=42) + self.assertEquals(response.status_code, 301) + self.assertEquals(response['Location'], '/bar/42/') diff --git a/tests/regressiontests/generic_views/dates.py b/tests/regressiontests/generic_views/dates.py new file mode 100644 index 0000000000..c730c763b9 --- /dev/null +++ b/tests/regressiontests/generic_views/dates.py @@ -0,0 +1,352 @@ +import datetime +import random + +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase + +from regressiontests.generic_views.models import Book + +class ArchiveIndexViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def _make_books(self, n, base_date): + for i in range(n): + b = Book.objects.create( + name='Book %d' % i, + slug='book-%d' % i, + pages=100+i, + pubdate=base_date - datetime.timedelta(days=1)) + + def test_archive_view(self): + res = self.client.get('/dates/books/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1]) + self.assertEqual(list(res.context['latest']), list(Book.objects.all())) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + def test_archive_view_context_object_name(self): + res = self.client.get('/dates/books/context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1]) + self.assertEqual(list(res.context['thingies']), list(Book.objects.all())) + self.assertFalse('latest' in res.context) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + def test_empty_archive_view(self): + Book.objects.all().delete() + res = self.client.get('/dates/books/') + self.assertEqual(res.status_code, 404) + + def test_allow_empty_archive_view(self): + Book.objects.all().delete() + res = self.client.get('/dates/books/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), []) + self.assertEqual(list(res.context['date_list']), []) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + def test_archive_view_template(self): + res = self.client.get('/dates/books/template_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1]) + self.assertEqual(list(res.context['latest']), list(Book.objects.all())) + self.assertTemplateUsed(res, 'generic_views/list.html') + + def test_archive_view_template_suffix(self): + res = self.client.get('/dates/books/template_name_suffix/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1]) + self.assertEqual(list(res.context['latest']), list(Book.objects.all())) + self.assertTemplateUsed(res, 'generic_views/book_detail.html') + + def test_archive_view_invalid(self): + self.assertRaises(ImproperlyConfigured, self.client.get, '/dates/books/invalid/') + + def test_paginated_archive_view(self): + self._make_books(20, base_date=datetime.date.today()) + res = self.client.get('/dates/books/paginated/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1]) + self.assertEqual(list(res.context['latest']), list(Book.objects.all()[0:10])) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + res = self.client.get('/dates/books/paginated/?page=2') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['page_obj'].number, 2) + self.assertEqual(list(res.context['latest']), list(Book.objects.all()[10:20])) + + +class YearArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def test_year_view(self): + res = self.client.get('/dates/books/2008/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), [datetime.datetime(2008, 10, 1)]) + self.assertEqual(res.context['year'], '2008') + self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') + + def test_year_view_make_object_list(self): + res = self.client.get('/dates/books/2006/make_object_list/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), [datetime.datetime(2006, 5, 1)]) + self.assertEqual(list(res.context['books']), list(Book.objects.filter(pubdate__year=2006))) + self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006))) + self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') + + def test_year_view_empty(self): + res = self.client.get('/dates/books/1999/') + self.assertEqual(res.status_code, 404) + res = self.client.get('/dates/books/1999/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), []) + self.assertEqual(list(res.context['books']), []) + + def test_year_view_allow_future(self): + # Create a new book in the future + year = datetime.date.today().year + 1 + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=datetime.date(year, 1, 1)) + res = self.client.get('/dates/books/%s/' % year) + self.assertEqual(res.status_code, 404) + + res = self.client.get('/dates/books/%s/allow_empty/' % year) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['books']), []) + + res = self.client.get('/dates/books/%s/allow_future/' % year) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), [datetime.datetime(year, 1, 1)]) + + def test_year_view_invalid_pattern(self): + res = self.client.get('/dates/books/no_year/') + self.assertEqual(res.status_code, 404) + +class MonthArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def test_month_view(self): + res = self.client.get('/dates/books/2008/oct/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/book_archive_month.html') + self.assertEqual(list(res.context['date_list']), [datetime.datetime(2008, 10, 1)]) + self.assertEqual(list(res.context['books']), + list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1)))) + self.assertEqual(res.context['month'], datetime.date(2008, 10, 1)) + + # Since allow_empty=False, next/prev months must be valid (#7164) + self.assertEqual(res.context['next_month'], None) + self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1)) + + def test_month_view_allow_empty(self): + # allow_empty = False, empty month + res = self.client.get('/dates/books/2000/jan/') + self.assertEqual(res.status_code, 404) + + # allow_empty = True, empty month + res = self.client.get('/dates/books/2000/jan/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), []) + self.assertEqual(list(res.context['books']), []) + self.assertEqual(res.context['month'], datetime.date(2000, 1, 1)) + + # Since it's allow empty, next/prev are allowed to be empty months (#7164) + self.assertEqual(res.context['next_month'], datetime.date(2000, 2, 1)) + self.assertEqual(res.context['previous_month'], datetime.date(1999, 12, 1)) + + # allow_empty but not allow_future: next_month should be empty (#7164) + url = datetime.date.today().strftime('/dates/books/%Y/%b/allow_empty/').lower() + res = self.client.get(url) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_month'], None) + + def test_month_view_allow_future(self): + future = (datetime.date.today() + datetime.timedelta(days=60)).replace(day=1) + urlbit = future.strftime('%Y/%b').lower() + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) + + # allow_future = False, future month + res = self.client.get('/dates/books/%s/' % urlbit) + self.assertEqual(res.status_code, 404) + + # allow_future = True, valid future month + res = self.client.get('/dates/books/%s/allow_future/' % urlbit) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'][0].date(), b.pubdate) + self.assertEqual(list(res.context['books']), [b]) + self.assertEqual(res.context['month'], future) + + # Since it's allow_future but not allow_empty, next/prev are not + # allowed to be empty months (#7164) + self.assertEqual(res.context['next_month'], None) + self.assertEqual(res.context['previous_month'], datetime.date(2008, 10, 1)) + + # allow_future, but not allow_empty, with a current month. So next + # should be in the future (yup, #7164, again) + res = self.client.get('/dates/books/2008/oct/allow_future/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_month'], future) + self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1)) + + def test_custom_month_format(self): + res = self.client.get('/dates/books/2008/10/') + self.assertEqual(res.status_code, 200) + + def test_month_view_invalid_pattern(self): + res = self.client.get('/dates/books/2007/no_month/') + self.assertEqual(res.status_code, 404) + +class WeekArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def test_week_view(self): + res = self.client.get('/dates/books/2008/week/39/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/book_archive_week.html') + self.assertEqual(res.context['books'][0], Book.objects.get(pubdate=datetime.date(2008, 10, 1))) + self.assertEqual(res.context['week'], datetime.date(2008, 9, 28)) + + def test_week_view_allow_empty(self): + res = self.client.get('/dates/books/2008/week/12/') + self.assertEqual(res.status_code, 404) + + res = self.client.get('/dates/books/2008/week/12/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['books']), []) + + def test_week_view_allow_future(self): + future = datetime.date(datetime.date.today().year + 1, 1, 1) + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) + + res = self.client.get('/dates/books/%s/week/0/' % future.year) + self.assertEqual(res.status_code, 404) + + res = self.client.get('/dates/books/%s/week/0/allow_future/' % future.year) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['books']), [b]) + + def test_week_view_invalid_pattern(self): + res = self.client.get('/dates/books/2007/week/no_week/') + self.assertEqual(res.status_code, 404) + +class DayArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def test_day_view(self): + res = self.client.get('/dates/books/2008/oct/01/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/book_archive_day.html') + self.assertEqual(list(res.context['books']), + list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1)))) + self.assertEqual(res.context['day'], datetime.date(2008, 10, 1)) + + # Since allow_empty=False, next/prev days must be valid. + self.assertEqual(res.context['next_day'], None) + self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1)) + + def test_day_view_allow_empty(self): + # allow_empty = False, empty month + res = self.client.get('/dates/books/2000/jan/1/') + self.assertEqual(res.status_code, 404) + + # allow_empty = True, empty month + res = self.client.get('/dates/books/2000/jan/1/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['books']), []) + self.assertEqual(res.context['day'], datetime.date(2000, 1, 1)) + + # Since it's allow empty, next/prev are allowed to be empty months (#7164) + self.assertEqual(res.context['next_day'], datetime.date(2000, 1, 2)) + self.assertEqual(res.context['previous_day'], datetime.date(1999, 12, 31)) + + # allow_empty but not allow_future: next_month should be empty (#7164) + url = datetime.date.today().strftime('/dates/books/%Y/%b/%d/allow_empty/').lower() + res = self.client.get(url) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_day'], None) + + def test_day_view_allow_future(self): + future = (datetime.date.today() + datetime.timedelta(days=60)) + urlbit = future.strftime('%Y/%b/%d').lower() + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) + + # allow_future = False, future month + res = self.client.get('/dates/books/%s/' % urlbit) + self.assertEqual(res.status_code, 404) + + # allow_future = True, valid future month + res = self.client.get('/dates/books/%s/allow_future/' % urlbit) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['books']), [b]) + self.assertEqual(res.context['day'], future) + + # allow_future but not allow_empty, next/prev amust be valid + self.assertEqual(res.context['next_day'], None) + self.assertEqual(res.context['previous_day'], datetime.date(2008, 10, 1)) + + # allow_future, but not allow_empty, with a current month. + res = self.client.get('/dates/books/2008/oct/01/allow_future/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_day'], future) + self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1)) + + def test_next_prev_context(self): + res = self.client.get('/dates/books/2008/oct/01/') + self.assertEqual(res.content, "Archive for Oct. 1, 2008. Previous day is May 1, 2006") + + def test_custom_month_format(self): + res = self.client.get('/dates/books/2008/10/01/') + self.assertEqual(res.status_code, 200) + + def test_day_view_invalid_pattern(self): + res = self.client.get('/dates/books/2007/oct/no_day/') + self.assertEqual(res.status_code, 404) + + def test_today_view(self): + res = self.client.get('/dates/books/today/') + self.assertEqual(res.status_code, 404) + res = self.client.get('/dates/books/today/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['day'], datetime.date.today()) + +class DateDetailViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def test_date_detail_by_pk(self): + res = self.client.get('/dates/books/2008/oct/01/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Book.objects.get(pk=1)) + self.assertEqual(res.context['book'], Book.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/book_detail.html') + + def test_date_detail_by_slug(self): + res = self.client.get('/dates/books/2006/may/01/byslug/dreaming-in-code/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['book'], Book.objects.get(slug='dreaming-in-code')) + + def test_date_detail_custom_month_format(self): + res = self.client.get('/dates/books/2008/10/01/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['book'], Book.objects.get(pk=1)) + + def test_date_detail_allow_future(self): + future = (datetime.date.today() + datetime.timedelta(days=60)) + urlbit = future.strftime('%Y/%b/%d').lower() + b = Book.objects.create(name="The New New Testement", slug="new-new", pages=600, pubdate=future) + + res = self.client.get('/dates/books/%s/new-new/' % urlbit) + self.assertEqual(res.status_code, 404) + + res = self.client.get('/dates/books/%s/%s/allow_future/' % (urlbit, b.id)) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['book'], b) + self.assertTemplateUsed(res, 'generic_views/book_detail.html') + + def test_invalid_url(self): + self.assertRaises(AttributeError, self.client.get, "/dates/books/2008/oct/01/nopk/") + diff --git a/tests/regressiontests/generic_views/detail.py b/tests/regressiontests/generic_views/detail.py new file mode 100644 index 0000000000..91cacf65ba --- /dev/null +++ b/tests/regressiontests/generic_views/detail.py @@ -0,0 +1,71 @@ +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase + +from regressiontests.generic_views.models import Author, Page + + +class DetailViewTest(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def test_simple_object(self): + res = self.client.get('/detail/obj/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], {'foo': 'bar'}) + self.assertTemplateUsed(res, 'generic_views/detail.html') + + def test_detail_by_pk(self): + res = self.client.get('/detail/author/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_detail_by_slug(self): + res = self.client.get('/detail/author/byslug/scott-rosenberg/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(slug='scott-rosenberg')) + self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_template_name(self): + res = self.client.get('/detail/author/1/template_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/about.html') + + def test_template_name_suffix(self): + res = self.client.get('/detail/author/1/template_name_suffix/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/author_view.html') + + def test_template_name_field(self): + res = self.client.get('/detail/page/1/field/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Page.objects.get(pk=1)) + self.assertEqual(res.context['page'], Page.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/page_template.html') + + def test_context_object_name(self): + res = self.client.get('/detail/author/1/context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['thingy'], Author.objects.get(pk=1)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_duplicated_context_object_name(self): + res = self.client.get('/detail/author/1/dupe_context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_invalid_url(self): + self.assertRaises(AttributeError, self.client.get, '/detail/author/invalid/url/') + + def test_invalid_queryset(self): + self.assertRaises(ImproperlyConfigured, self.client.get, '/detail/author/invalid/qs/') diff --git a/tests/regressiontests/generic_views/edit.py b/tests/regressiontests/generic_views/edit.py new file mode 100644 index 0000000000..87233ec357 --- /dev/null +++ b/tests/regressiontests/generic_views/edit.py @@ -0,0 +1,233 @@ +from django.core.exceptions import ImproperlyConfigured +from django import forms +from django.test import TestCase +from django.utils.unittest import expectedFailure + +from regressiontests.generic_views.models import Artist, Author +from regressiontests.generic_views import views + + +class CreateViewTests(TestCase): + urls = 'regressiontests.generic_views.urls' + + def test_create(self): + res = self.client.get('/edit/authors/create/') + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertFalse('object' in res.context) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + + res = self.client.post('/edit/authors/create/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>']) + + def test_create_invalid(self): + res = self.client.post('/edit/authors/create/', + {'name': 'A' * 101, 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + self.assertEqual(len(res.context['form'].errors), 1) + self.assertEqual(Author.objects.count(), 0) + + def test_create_with_object_url(self): + res = self.client.post('/edit/artists/create/', + {'name': 'Rene Magritte'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/detail/artist/1/') + self.assertQuerysetEqual(Artist.objects.all(), ['<Artist: Rene Magritte>']) + + def test_create_with_redirect(self): + res = self.client.post('/edit/authors/create/redirect/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/edit/authors/create/') + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>']) + + def test_create_with_special_properties(self): + res = self.client.get('/edit/authors/create/special/') + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], views.AuthorForm)) + self.assertFalse('object' in res.context) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/form.html') + + res = self.client.post('/edit/authors/create/special/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/detail/author/1/') + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>']) + + def test_create_without_redirect(self): + try: + res = self.client.post('/edit/authors/create/naive/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') + except ImproperlyConfigured: + pass + + def test_create_restricted(self): + res = self.client.post('/edit/authors/create/restricted/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') + +class UpdateViewTests(TestCase): + urls = 'regressiontests.generic_views.urls' + + def test_update_post(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.get('/edit/author/1/update/') + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + + # Modification with both POST and PUT (browser compatible) + res = self.client.post('/edit/author/1/update/', + {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (xkcd)>']) + + @expectedFailure + def test_update_put(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.get('/edit/author/1/update/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + + res = self.client.put('/edit/author/1/update/', + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>']) + + def test_update_invalid(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/1/update/', + {'name': 'A' * 101, 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + self.assertEqual(len(res.context['form'].errors), 1) + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>']) + + def test_update_with_object_url(self): + a = Artist.objects.create(name='Rene Magritte') + res = self.client.post('/edit/artists/1/update/', + {'name': 'Rene Magritte'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/detail/artist/1/') + self.assertQuerysetEqual(Artist.objects.all(), ['<Artist: Rene Magritte>']) + + def test_update_with_redirect(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/1/update/redirect/', + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/edit/authors/create/') + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>']) + + def test_update_with_special_properties(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.get('/edit/author/1/update/special/') + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], views.AuthorForm)) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['thingy'], Author.objects.get(pk=1)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/form.html') + + res = self.client.post('/edit/author/1/update/special/', + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/detail/author/1/') + self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>']) + + def test_update_without_redirect(self): + try: + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/1/update/naive/', + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') + except ImproperlyConfigured: + pass + +class DeleteViewTests(TestCase): + urls = 'regressiontests.generic_views.urls' + + def test_delete_by_post(self): + Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.get('/edit/author/1/delete/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/author_confirm_delete.html') + + # Deletion with POST + res = self.client.post('/edit/author/1/delete/') + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_by_delete(self): + # Deletion with browser compatible DELETE method + Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.delete('/edit/author/1/delete/') + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_with_redirect(self): + Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.post('/edit/author/1/delete/redirect/') + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/edit/authors/create/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_with_special_properties(self): + Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.get('/edit/author/1/delete/special/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['thingy'], Author.objects.get(pk=1)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/confirm_delete.html') + + res = self.client.post('/edit/author/1/delete/special/') + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_without_redirect(self): + try: + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/1/delete/naive/') + self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') + except ImproperlyConfigured: + pass + diff --git a/tests/regressiontests/generic_views/fixtures/generic-views-test-data.json b/tests/regressiontests/generic_views/fixtures/generic-views-test-data.json new file mode 100644 index 0000000000..8ecfe5e1e2 --- /dev/null +++ b/tests/regressiontests/generic_views/fixtures/generic-views-test-data.json @@ -0,0 +1,47 @@ +[ + { + "model": "generic_views.author", + "pk": 1, + "fields": { + "name": "Roberto BolaƱo", + "slug": "roberto-bolano" + } + }, + { + "model": "generic_views.author", + "pk": 2, + "fields": { + "name": "Scott Rosenberg", + "slug": "scott-rosenberg" + } + }, + { + "model": "generic_views.book", + "pk": 1, + "fields": { + "name": "2066", + "slug": "2066", + "pages": "800", + "authors": [1], + "pubdate": "2008-10-01" + } + }, + { + "model": "generic_views.book", + "pk": 2, + "fields": { + "name": "Dreaming in Code", + "slug": "dreaming-in-code", + "pages": "300", + "pubdate": "2006-05-01" + } + }, + { + "model": "generic_views.page", + "pk": 1, + "fields": { + "template": "generic_views/page_template.html", + "content": "I was once bitten by a moose." + } + } +]
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/forms.py b/tests/regressiontests/generic_views/forms.py new file mode 100644 index 0000000000..7200947781 --- /dev/null +++ b/tests/regressiontests/generic_views/forms.py @@ -0,0 +1,11 @@ +from django import forms + +from regressiontests.generic_views.models import Author + + +class AuthorForm(forms.ModelForm): + name = forms.CharField() + slug = forms.SlugField() + + class Meta: + model = Author diff --git a/tests/regressiontests/generic_views/list.py b/tests/regressiontests/generic_views/list.py new file mode 100644 index 0000000000..5d62aa8fb8 --- /dev/null +++ b/tests/regressiontests/generic_views/list.py @@ -0,0 +1,129 @@ +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase + +from regressiontests.generic_views.models import Author + + +class ListViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'regressiontests.generic_views.urls' + + def test_items(self): + res = self.client.get('/list/dict/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/list.html') + self.assertEqual(res.context['object_list'][0]['first'], 'John') + + def test_queryset(self): + res = self.client.get('/list/authors/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertEqual(list(res.context['authors']), list(Author.objects.all())) + self.assertEqual(res.context['paginator'], None) + self.assertEqual(res.context['page_obj'], None) + self.assertEqual(res.context['is_paginated'], False) + + def test_paginated_queryset(self): + self._make_authors(100) + res = self.client.get('/list/authors/paginated/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(len(res.context['authors']), 30) + self.assertEqual(res.context['is_paginated'], True) + self.assertEqual(res.context['page_obj'].number, 1) + self.assertEqual(res.context['paginator'].num_pages, 4) + self.assertEqual(res.context['authors'][0].name, 'Author 00') + self.assertEqual(list(res.context['authors'])[-1].name, 'Author 29') + + def test_paginated_queryset_shortdata(self): + res = self.client.get('/list/authors/paginated/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertEqual(list(res.context['authors']), list(Author.objects.all())) + self.assertEqual(res.context['paginator'], None) + self.assertEqual(res.context['page_obj'], None) + self.assertEqual(res.context['is_paginated'], False) + + def test_paginated_get_page_by_query_string(self): + self._make_authors(100) + res = self.client.get('/list/authors/paginated/', {'page': '2'}) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(len(res.context['authors']), 30) + self.assertEqual(res.context['authors'][0].name, 'Author 30') + self.assertEqual(res.context['page_obj'].number, 2) + + def test_paginated_get_last_page_by_query_string(self): + self._make_authors(100) + res = self.client.get('/list/authors/paginated/', {'page': 'last'}) + self.assertEqual(res.status_code, 200) + self.assertEqual(len(res.context['authors']), 10) + self.assertEqual(res.context['authors'][0].name, 'Author 90') + self.assertEqual(res.context['page_obj'].number, 4) + + def test_paginated_get_page_by_urlvar(self): + self._make_authors(100) + res = self.client.get('/list/authors/paginated/3/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(len(res.context['authors']), 30) + self.assertEqual(res.context['authors'][0].name, 'Author 60') + self.assertEqual(res.context['page_obj'].number, 3) + + def test_paginated_page_out_of_range(self): + self._make_authors(100) + res = self.client.get('/list/authors/paginated/42/') + self.assertEqual(res.status_code, 404) + + def test_paginated_invalid_page(self): + self._make_authors(100) + res = self.client.get('/list/authors/paginated/?page=frog') + self.assertEqual(res.status_code, 404) + + def test_allow_empty_false(self): + res = self.client.get('/list/authors/notempty/') + self.assertEqual(res.status_code, 200) + Author.objects.all().delete() + res = self.client.get('/list/authors/notempty/') + self.assertEqual(res.status_code, 404) + + def test_template_name(self): + res = self.client.get('/list/authors/template_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertEqual(list(res.context['authors']), list(Author.objects.all())) + self.assertTemplateUsed(res, 'generic_views/list.html') + + def test_template_name_suffix(self): + res = self.client.get('/list/authors/template_name_suffix/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertEqual(list(res.context['authors']), list(Author.objects.all())) + self.assertTemplateUsed(res, 'generic_views/author_objects.html') + + def test_context_object_name(self): + res = self.client.get('/list/authors/context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertEqual(list(res.context['author_list']), list(Author.objects.all())) + self.assertFalse('authors' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + + def test_duplicate_context_object_name(self): + res = self.client.get('/list/authors/dupe_context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertFalse('author_list' in res.context) + self.assertFalse('authors' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + + def test_missing_items(self): + self.assertRaises(ImproperlyConfigured, self.client.get, '/list/authors/invalid/') + + def _make_authors(self, n): + Author.objects.all().delete() + for i in range(n): + Author.objects.create(name='Author %02i' % i, slug='a%s' % i) + diff --git a/tests/regressiontests/generic_views/models.py b/tests/regressiontests/generic_views/models.py new file mode 100644 index 0000000000..5a8577d0f6 --- /dev/null +++ b/tests/regressiontests/generic_views/models.py @@ -0,0 +1,41 @@ +from django.db import models + +class Artist(models.Model): + name = models.CharField(max_length=100) + + class Meta: + ordering = ['name'] + + def __unicode__(self): + return self.name + + @models.permalink + def get_absolute_url(self): + return ('artist_detail', (), {'pk': self.id}) + +class Author(models.Model): + name = models.CharField(max_length=100) + slug = models.SlugField() + + class Meta: + ordering = ['name'] + + def __unicode__(self): + return self.name + +class Book(models.Model): + name = models.CharField(max_length=300) + slug = models.SlugField() + pages = models.IntegerField() + authors = models.ManyToManyField(Author) + pubdate = models.DateField() + + class Meta: + ordering = ['-pubdate'] + + def __unicode__(self): + return self.name + +class Page(models.Model): + content = models.TextField() + template = models.CharField(max_length=300) diff --git a/tests/regressiontests/generic_views/templates/generic_views/about.html b/tests/regressiontests/generic_views/templates/generic_views/about.html new file mode 100644 index 0000000000..7d877fdd78 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/about.html @@ -0,0 +1 @@ +<h1>About</h1>
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/apple_detail.html b/tests/regressiontests/generic_views/templates/generic_views/apple_detail.html new file mode 100644 index 0000000000..4fd0bd933b --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/apple_detail.html @@ -0,0 +1 @@ +This is a {% if tasty %}tasty {% endif %}{{ apple.color }} apple{{ extra }}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/artist_detail.html b/tests/regressiontests/generic_views/templates/generic_views/artist_detail.html new file mode 100644 index 0000000000..9ea13fed83 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/artist_detail.html @@ -0,0 +1 @@ +This is an {{ artist }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/artist_form.html b/tests/regressiontests/generic_views/templates/generic_views/artist_form.html new file mode 100644 index 0000000000..114e9bc5cc --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/artist_form.html @@ -0,0 +1 @@ +A form: {{ form }}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/author_confirm_delete.html b/tests/regressiontests/generic_views/templates/generic_views/author_confirm_delete.html new file mode 100644 index 0000000000..d6ed99846e --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/author_confirm_delete.html @@ -0,0 +1 @@ +Are you sure?
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/author_detail.html b/tests/regressiontests/generic_views/templates/generic_views/author_detail.html new file mode 100644 index 0000000000..b6a60fd492 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/author_detail.html @@ -0,0 +1 @@ +This is an {{ author }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/author_form.html b/tests/regressiontests/generic_views/templates/generic_views/author_form.html new file mode 100644 index 0000000000..114e9bc5cc --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/author_form.html @@ -0,0 +1 @@ +A form: {{ form }}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/author_list.html b/tests/regressiontests/generic_views/templates/generic_views/author_list.html new file mode 100644 index 0000000000..42ba331802 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/author_list.html @@ -0,0 +1,3 @@ +{% for item in object_list %} + {{ item }} +{% endfor %}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/author_objects.html b/tests/regressiontests/generic_views/templates/generic_views/author_objects.html new file mode 100644 index 0000000000..42ba331802 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/author_objects.html @@ -0,0 +1,3 @@ +{% for item in object_list %} + {{ item }} +{% endfor %}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/author_view.html b/tests/regressiontests/generic_views/templates/generic_views/author_view.html new file mode 100644 index 0000000000..dcd9d6b3a6 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/author_view.html @@ -0,0 +1 @@ +This is an alternate template_name_suffix for an {{ author }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/book_archive.html b/tests/regressiontests/generic_views/templates/generic_views/book_archive.html new file mode 100644 index 0000000000..5fc11149bd --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/book_archive.html @@ -0,0 +1 @@ +Archive of books from {{ date_list }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/book_archive_day.html b/tests/regressiontests/generic_views/templates/generic_views/book_archive_day.html new file mode 100644 index 0000000000..4a7b5027b5 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/book_archive_day.html @@ -0,0 +1 @@ +Archive for {{ day }}. Previous day is {{ previous_day }}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/book_archive_month.html b/tests/regressiontests/generic_views/templates/generic_views/book_archive_month.html new file mode 100644 index 0000000000..4e1e52b35c --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/book_archive_month.html @@ -0,0 +1 @@ +Books in {{ month }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/book_archive_week.html b/tests/regressiontests/generic_views/templates/generic_views/book_archive_week.html new file mode 100644 index 0000000000..0ddcbc98a9 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/book_archive_week.html @@ -0,0 +1 @@ +Archive for {{ week }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/book_archive_year.html b/tests/regressiontests/generic_views/templates/generic_views/book_archive_year.html new file mode 100644 index 0000000000..7705914e97 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/book_archive_year.html @@ -0,0 +1 @@ +Archive of books from {{ year }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/book_detail.html b/tests/regressiontests/generic_views/templates/generic_views/book_detail.html new file mode 100644 index 0000000000..c40af70426 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/book_detail.html @@ -0,0 +1 @@ +This is {{ book }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/book_list.html b/tests/regressiontests/generic_views/templates/generic_views/book_list.html new file mode 100644 index 0000000000..42ba331802 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/book_list.html @@ -0,0 +1,3 @@ +{% for item in object_list %} + {{ item }} +{% endfor %}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/confirm_delete.html b/tests/regressiontests/generic_views/templates/generic_views/confirm_delete.html new file mode 100644 index 0000000000..87288c4495 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/confirm_delete.html @@ -0,0 +1 @@ +Generic: Are you sure?
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/detail.html b/tests/regressiontests/generic_views/templates/generic_views/detail.html new file mode 100644 index 0000000000..1b6b9d946b --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/detail.html @@ -0,0 +1 @@ +Look, an {{ object }}.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/form.html b/tests/regressiontests/generic_views/templates/generic_views/form.html new file mode 100644 index 0000000000..7249998643 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/form.html @@ -0,0 +1 @@ +A generic form: {{ form }}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/list.html b/tests/regressiontests/generic_views/templates/generic_views/list.html new file mode 100644 index 0000000000..42ba331802 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/list.html @@ -0,0 +1,3 @@ +{% for item in object_list %} + {{ item }} +{% endfor %}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/generic_views/page_template.html b/tests/regressiontests/generic_views/templates/generic_views/page_template.html new file mode 100644 index 0000000000..88e20c8b50 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/generic_views/page_template.html @@ -0,0 +1 @@ +This is some content: {{ content }}
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/templates/registration/login.html b/tests/regressiontests/generic_views/templates/registration/login.html new file mode 100644 index 0000000000..cac78221e2 --- /dev/null +++ b/tests/regressiontests/generic_views/templates/registration/login.html @@ -0,0 +1 @@ +An empty login template.
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/tests.py b/tests/regressiontests/generic_views/tests.py new file mode 100644 index 0000000000..40eec72b25 --- /dev/null +++ b/tests/regressiontests/generic_views/tests.py @@ -0,0 +1,5 @@ +from regressiontests.generic_views.base import ViewTest, TemplateViewTest, RedirectViewTest +from regressiontests.generic_views.dates import ArchiveIndexViewTests, YearArchiveViewTests, MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests, DateDetailViewTests +from regressiontests.generic_views.detail import DetailViewTest +from regressiontests.generic_views.edit import CreateViewTests, UpdateViewTests, DeleteViewTests +from regressiontests.generic_views.list import ListViewTests
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/urls.py b/tests/regressiontests/generic_views/urls.py new file mode 100644 index 0000000000..548d10d164 --- /dev/null +++ b/tests/regressiontests/generic_views/urls.py @@ -0,0 +1,186 @@ +from django.conf.urls.defaults import * +from django.views.generic import TemplateView + +import views + + +urlpatterns = patterns('', + # base + #(r'^about/login-required/$', + # views.DecoratedAboutView()), + + # TemplateView + (r'^template/simple/(?P<foo>\w+)/$', + TemplateView.as_view(template_name='generic_views/about.html')), + (r'^template/custom/(?P<foo>\w+)/$', + views.CustomTemplateView.as_view(template_name='generic_views/about.html')), + + # DetailView + (r'^detail/obj/$', + views.ObjectDetail.as_view()), + url(r'^detail/artist/(?P<pk>\d+)/$', + views.ArtistDetail.as_view(), + name="artist_detail"), + url(r'^detail/author/(?P<pk>\d+)/$', + views.AuthorDetail.as_view(), + name="author_detail"), + (r'^detail/author/byslug/(?P<slug>[\w-]+)/$', + views.AuthorDetail.as_view()), + (r'^detail/author/(?P<pk>\d+)/template_name_suffix/$', + views.AuthorDetail.as_view(template_name_suffix='_view')), + (r'^detail/author/(?P<pk>\d+)/template_name/$', + views.AuthorDetail.as_view(template_name='generic_views/about.html')), + (r'^detail/author/(?P<pk>\d+)/context_object_name/$', + views.AuthorDetail.as_view(context_object_name='thingy')), + (r'^detail/author/(?P<pk>\d+)/dupe_context_object_name/$', + views.AuthorDetail.as_view(context_object_name='object')), + (r'^detail/page/(?P<pk>\d+)/field/$', + views.PageDetail.as_view()), + (r'^detail/author/invalid/url/$', + views.AuthorDetail.as_view()), + (r'^detail/author/invalid/qs/$', + views.AuthorDetail.as_view(queryset=None)), + + # Create/UpdateView + (r'^edit/artists/create/$', + views.ArtistCreate.as_view()), + (r'^edit/artists/(?P<pk>\d+)/update/$', + views.ArtistUpdate.as_view()), + + (r'^edit/authors/create/naive/$', + views.NaiveAuthorCreate.as_view()), + (r'^edit/authors/create/redirect/$', + views.NaiveAuthorCreate.as_view(success_url='/edit/authors/create/')), + (r'^edit/authors/create/restricted/$', + views.AuthorCreateRestricted.as_view()), + (r'^edit/authors/create/$', + views.AuthorCreate.as_view()), + (r'^edit/authors/create/special/$', + views.SpecializedAuthorCreate.as_view()), + + (r'^edit/author/(?P<pk>\d+)/update/naive/$', + views.NaiveAuthorUpdate.as_view()), + (r'^edit/author/(?P<pk>\d+)/update/redirect/$', + views.NaiveAuthorUpdate.as_view(success_url='/edit/authors/create/')), + (r'^edit/author/(?P<pk>\d+)/update/$', + views.AuthorUpdate.as_view()), + (r'^edit/author/(?P<pk>\d+)/update/special/$', + views.SpecializedAuthorUpdate.as_view()), + (r'^edit/author/(?P<pk>\d+)/delete/naive/$', + views.NaiveAuthorDelete.as_view()), + (r'^edit/author/(?P<pk>\d+)/delete/redirect/$', + views.NaiveAuthorDelete.as_view(success_url='/edit/authors/create/')), + (r'^edit/author/(?P<pk>\d+)/delete/$', + views.AuthorDelete.as_view()), + (r'^edit/author/(?P<pk>\d+)/delete/special/$', + views.SpecializedAuthorDelete.as_view()), + + # ArchiveIndexView + (r'^dates/books/$', + views.BookArchive.as_view()), + (r'^dates/books/context_object_name/$', + views.BookArchive.as_view(context_object_name='thingies')), + (r'^dates/books/allow_empty/$', + views.BookArchive.as_view(allow_empty=True)), + (r'^dates/books/template_name/$', + views.BookArchive.as_view(template_name='generic_views/list.html')), + (r'^dates/books/template_name_suffix/$', + views.BookArchive.as_view(template_name_suffix='_detail')), + (r'^dates/books/invalid/$', + views.BookArchive.as_view(queryset=None)), + (r'^dates/books/paginated/$', + views.BookArchive.as_view(paginate_by=10)), + + # ListView + (r'^list/dict/$', + views.DictList.as_view()), + url(r'^list/authors/$', + views.AuthorList.as_view(), + name="authors_list"), + (r'^list/authors/paginated/$', + views.AuthorList.as_view(paginate_by=30)), + (r'^list/authors/paginated/(?P<page>\d+)/$', + views.AuthorList.as_view(paginate_by=30)), + (r'^list/authors/notempty/$', + views.AuthorList.as_view(allow_empty=False)), + (r'^list/authors/template_name/$', + views.AuthorList.as_view(template_name='generic_views/list.html')), + (r'^list/authors/template_name_suffix/$', + views.AuthorList.as_view(template_name_suffix='_objects')), + (r'^list/authors/context_object_name/$', + views.AuthorList.as_view(context_object_name='author_list')), + (r'^list/authors/dupe_context_object_name/$', + views.AuthorList.as_view(context_object_name='object_list')), + (r'^list/authors/invalid/$', + views.AuthorList.as_view(queryset=None)), + + # YearArchiveView + # Mixing keyword and possitional captures below is intentional; the views + # ought to be able to accept either. + (r'^dates/books/(?P<year>\d{4})/$', + views.BookYearArchive.as_view()), + (r'^dates/books/(?P<year>\d{4})/make_object_list/$', + views.BookYearArchive.as_view(make_object_list=True)), + (r'^dates/books/(?P<year>\d{4})/allow_empty/$', + views.BookYearArchive.as_view(allow_empty=True)), + (r'^dates/books/(?P<year>\d{4})/allow_future/$', + views.BookYearArchive.as_view(allow_future=True)), + (r'^dates/books/no_year/$', + views.BookYearArchive.as_view()), + + # MonthArchiveView + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/$', + views.BookMonthArchive.as_view()), + (r'^dates/books/(?P<year>\d{4})/(?P<month>\d{1,2})/$', + views.BookMonthArchive.as_view(month_format='%m')), + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/allow_empty/$', + views.BookMonthArchive.as_view(allow_empty=True)), + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/allow_future/$', + views.BookMonthArchive.as_view(allow_future=True)), + (r'^dates/books/(?P<year>\d{4})/no_month/$', + views.BookMonthArchive.as_view()), + + # WeekArchiveView + (r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/$', + views.BookWeekArchive.as_view()), + (r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/allow_empty/$', + views.BookWeekArchive.as_view(allow_empty=True)), + (r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/allow_future/$', + views.BookWeekArchive.as_view(allow_future=True)), + (r'^dates/books/(?P<year>\d{4})/week/no_week/$', + views.BookWeekArchive.as_view()), + + # DayArchiveView + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/$', + views.BookDayArchive.as_view()), + (r'^dates/books/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', + views.BookDayArchive.as_view(month_format='%m')), + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/allow_empty/$', + views.BookDayArchive.as_view(allow_empty=True)), + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/allow_future/$', + views.BookDayArchive.as_view(allow_future=True)), + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/no_day/$', + views.BookDayArchive.as_view()), + + # TodayArchiveView + (r'dates/books/today/$', + views.BookTodayArchive.as_view()), + (r'dates/books/today/allow_empty/$', + views.BookTodayArchive.as_view(allow_empty=True)), + + # DateDetailView + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$', + views.BookDetail.as_view()), + (r'^dates/books/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<pk>\d+)/$', + views.BookDetail.as_view(month_format='%m')), + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/allow_future/$', + views.BookDetail.as_view(allow_future=True)), + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/nopk/$', + views.BookDetail.as_view()), + + (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/byslug/(?P<slug>[\w-]+)/$', + views.BookDetail.as_view()), + + # Useful for testing redirects + (r'^accounts/login/$', 'django.contrib.auth.views.login') +)
\ No newline at end of file diff --git a/tests/regressiontests/generic_views/views.py b/tests/regressiontests/generic_views/views.py new file mode 100644 index 0000000000..eacfb717e2 --- /dev/null +++ b/tests/regressiontests/generic_views/views.py @@ -0,0 +1,145 @@ +from django.contrib.auth.decorators import login_required +from django.core.urlresolvers import reverse +from django.utils.decorators import method_decorator +from django.views import generic + +from regressiontests.generic_views.models import Artist, Author, Book, Page +from regressiontests.generic_views.forms import AuthorForm + + +class CustomTemplateView(generic.TemplateView): + template_name = 'generic_views/about.html' + + def get_context_data(self, **kwargs): + return { + 'params': kwargs, + 'key': 'value' + } + + +class ObjectDetail(generic.DetailView): + template_name = 'generic_views/detail.html' + + def get_object(self): + return {'foo': 'bar'} + + +class ArtistDetail(generic.DetailView): + queryset = Artist.objects.all() + + +class AuthorDetail(generic.DetailView): + queryset = Author.objects.all() + + +class PageDetail(generic.DetailView): + queryset = Page.objects.all() + template_name_field = 'template' + + +class DictList(generic.ListView): + """A ListView that doesn't use a model.""" + queryset = [ + {'first': 'John', 'last': 'Lennon'}, + {'last': 'Yoko', 'last': 'Ono'} + ] + template_name = 'generic_views/list.html' + + +class AuthorList(generic.ListView): + queryset = Author.objects.all() + + + +class ArtistCreate(generic.CreateView): + model = Artist + + +class NaiveAuthorCreate(generic.CreateView): + queryset = Author.objects.all() + + +class AuthorCreate(generic.CreateView): + model = Author + success_url = '/list/authors/' + + +class SpecializedAuthorCreate(generic.CreateView): + model = Author + form_class = AuthorForm + template_name = 'generic_views/form.html' + context_object_name = 'thingy' + + def get_success_url(self): + return reverse('author_detail', args=[self.object.id,]) + + +class AuthorCreateRestricted(AuthorCreate): + post = method_decorator(login_required)(AuthorCreate.post) + + +class ArtistUpdate(generic.UpdateView): + model = Artist + + +class NaiveAuthorUpdate(generic.UpdateView): + queryset = Author.objects.all() + + +class AuthorUpdate(generic.UpdateView): + model = Author + success_url = '/list/authors/' + + +class SpecializedAuthorUpdate(generic.UpdateView): + model = Author + form_class = AuthorForm + template_name = 'generic_views/form.html' + context_object_name = 'thingy' + + def get_success_url(self): + return reverse('author_detail', args=[self.object.id,]) + + +class NaiveAuthorDelete(generic.DeleteView): + queryset = Author.objects.all() + + +class AuthorDelete(generic.DeleteView): + model = Author + success_url = '/list/authors/' + + +class SpecializedAuthorDelete(generic.DeleteView): + queryset = Author.objects.all() + template_name = 'generic_views/confirm_delete.html' + context_object_name = 'thingy' + + def get_success_url(self): + return reverse('authors_list') + + +class BookConfig(object): + queryset = Book.objects.all() + date_field = 'pubdate' + +class BookArchive(BookConfig, generic.ArchiveIndexView): + pass + +class BookYearArchive(BookConfig, generic.YearArchiveView): + pass + +class BookMonthArchive(BookConfig, generic.MonthArchiveView): + pass + +class BookWeekArchive(BookConfig, generic.WeekArchiveView): + pass + +class BookDayArchive(BookConfig, generic.DayArchiveView): + pass + +class BookTodayArchive(BookConfig, generic.TodayArchiveView): + pass + +class BookDetail(BookConfig, generic.DateDetailView): + pass |
