diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/modeltests/generic_relations/models.py | 5 | ||||
| -rw-r--r-- | tests/regressiontests/admin_views/models.py | 56 | ||||
| -rw-r--r-- | tests/regressiontests/admin_views/tests.py | 152 |
3 files changed, 174 insertions, 39 deletions
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py index e6c1ee243e..1bc6577bf7 100644 --- a/tests/modeltests/generic_relations/models.py +++ b/tests/modeltests/generic_relations/models.py @@ -238,4 +238,9 @@ __test__ = {'API_TESTS':""" <p><label for="id_generic_relations-taggeditem-content_type-object_id-1-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-1-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-1-tag" maxlength="50" /></p> <p><label for="id_generic_relations-taggeditem-content_type-object_id-1-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-1-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-1-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-1-id" id="id_generic_relations-taggeditem-content_type-object_id-1-id" /></p> +>>> formset = GenericFormSet(instance=lion, prefix='x') +>>> for form in formset.forms: +... print form.as_p() +<p><label for="id_x-0-tag">Tag:</label> <input id="id_x-0-tag" type="text" name="x-0-tag" maxlength="50" /></p> +<p><label for="id_x-0-DELETE">Delete:</label> <input type="checkbox" name="x-0-DELETE" id="id_x-0-DELETE" /><input type="hidden" name="x-0-id" id="id_x-0-id" /></p> """} diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index 050823f6de..d849a7b9c1 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -20,7 +20,7 @@ class Article(models.Model): def __unicode__(self): return self.title - + def model_year(self): return self.date.year model_year.admin_order_field = 'date' @@ -54,14 +54,14 @@ class Chapter(models.Model): class ChapterXtra1(models.Model): chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?') - xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?') + xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?') def __unicode__(self): return u'¿Xtra1: %s' % self.xtra class ChapterXtra2(models.Model): chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?') - xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?') + xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?') def __unicode__(self): return u'¿Xtra2: %s' % self.xtra @@ -87,7 +87,7 @@ class ArticleAdmin(admin.ModelAdmin): 'extra_var': 'Hello!' } ) - + def modeladmin_year(self, obj): return obj.date.year modeladmin_year.admin_order_field = 'date' @@ -121,7 +121,7 @@ class ModelWithStringPrimaryKey(models.Model): class Color(models.Model): value = models.CharField(max_length=10) - warm = models.BooleanField() + warm = models.BooleanField() def __unicode__(self): return self.value @@ -134,12 +134,56 @@ class Thing(models.Model): class ThingAdmin(admin.ModelAdmin): list_filter = ('color',) +class Persona(models.Model): + """ + A simple persona associated with accounts, to test inlining of related + accounts which inherit from a common accounts class. + """ + name = models.CharField(blank=False, max_length=80) + def __unicode__(self): + return self.name + +class Account(models.Model): + """ + A simple, generic account encapsulating the information shared by all + types of accounts. + """ + username = models.CharField(blank=False, max_length=80) + persona = models.ForeignKey(Persona, related_name="accounts") + servicename = u'generic service' + + def __unicode__(self): + return "%s: %s" % (self.servicename, self.username) + +class FooAccount(Account): + """A service-specific account of type Foo.""" + servicename = u'foo' + +class BarAccount(Account): + """A service-specific account of type Bar.""" + servicename = u'bar' + +class FooAccountAdmin(admin.StackedInline): + model = FooAccount + extra = 1 + +class BarAccountAdmin(admin.StackedInline): + model = BarAccount + extra = 1 + +class PersonaAdmin(admin.ModelAdmin): + inlines = ( + FooAccountAdmin, + BarAccountAdmin + ) + admin.site.register(Article, ArticleAdmin) admin.site.register(CustomArticle, CustomArticleAdmin) admin.site.register(Section, inlines=[ArticleInline]) admin.site.register(ModelWithStringPrimaryKey) admin.site.register(Color) admin.site.register(Thing, ThingAdmin) +admin.site.register(Persona, PersonaAdmin) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: @@ -153,3 +197,5 @@ admin.site.register(Thing, ThingAdmin) admin.site.register(Book, inlines=[ChapterInline]) admin.site.register(Promo) admin.site.register(ChapterXtra1) + + diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 39daf116ab..bf198bc9a2 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -1,5 +1,7 @@ # coding: utf-8 +import re + from django.test import TestCase from django.contrib.auth.models import User, Permission from django.contrib.contenttypes.models import ContentType @@ -9,22 +11,27 @@ from django.contrib.admin.util import quote from django.utils.html import escape # local test models -from models import Article, CustomArticle, Section, ModelWithStringPrimaryKey +from models import Article, CustomArticle, Section, ModelWithStringPrimaryKey, Persona, FooAccount, BarAccount + +try: + set +except NameError: + from sets import Set as set class AdminViewBasicTest(TestCase): fixtures = ['admin-views-users.xml', 'admin-views-colors.xml'] - - # Store the bit of the URL where the admin is registered as a class + + # Store the bit of the URL where the admin is registered as a class # variable. That way we can test a second AdminSite just by subclassing # this test case and changing urlbit. urlbit = 'admin' - + def setUp(self): self.client.login(username='super', password='secret') - + def tearDown(self): self.client.logout() - + def testTrailingSlashRequired(self): """ If you leave off the trailing slash, app should redirect and add it. @@ -33,29 +40,29 @@ class AdminViewBasicTest(TestCase): self.assertRedirects(request, '/test_admin/%s/admin_views/article/add/' % self.urlbit, status_code=301 ) - + def testBasicAddGet(self): """ A smoke test to ensure GET on the add_view works. """ response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit) self.failUnlessEqual(response.status_code, 200) - + def testAddWithGETArgs(self): response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit, {'name': 'My Section'}) self.failUnlessEqual(response.status_code, 200) self.failUnless( - 'value="My Section"' in response.content, + 'value="My Section"' in response.content, "Couldn't find an input with the right value in the response." ) - + def testBasicEditGet(self): """ A smoke test to ensureGET on the change_view works. """ response = self.client.get('/test_admin/%s/admin_views/section/1/' % self.urlbit) self.failUnlessEqual(response.status_code, 200) - + def testBasicAddPost(self): """ A smoke test to ensure POST on add_view works. @@ -68,7 +75,7 @@ class AdminViewBasicTest(TestCase): } response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere - + def testBasicEditPost(self): """ A smoke test to ensure POST on edit_view works. @@ -116,7 +123,7 @@ class AdminViewBasicTest(TestCase): def testChangeListSortingCallable(self): """ - Ensure we can sort on a list_display field that is a callable + Ensure we can sort on a list_display field that is a callable (column 2 is callable_year in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'asc', 'o': 2}) @@ -126,10 +133,10 @@ class AdminViewBasicTest(TestCase): response.content.index('Middle content') < response.content.index('Newest content'), "Results of sorting on callable are out of order." ) - + def testChangeListSortingModel(self): """ - Ensure we can sort on a list_display field that is a Model method + Ensure we can sort on a list_display field that is a Model method (colunn 3 is 'model_year' in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'dsc', 'o': 3}) @@ -139,37 +146,37 @@ class AdminViewBasicTest(TestCase): response.content.index('Middle content') < response.content.index('Oldest content'), "Results of sorting on Model method are out of order." ) - + def testChangeListSortingModelAdmin(self): """ - Ensure we can sort on a list_display field that is a ModelAdmin method + Ensure we can sort on a list_display field that is a ModelAdmin method (colunn 4 is 'modeladmin_year' in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'asc', 'o': 4}) self.failUnlessEqual(response.status_code, 200) self.failUnless( - response.content.index('Oldest content') < response.content.index('Middle content') and + response.content.index('Oldest content') < response.content.index('Middle content') and response.content.index('Middle content') < response.content.index('Newest content'), "Results of sorting on ModelAdmin method are out of order." ) - + def testLimitedFilter(self): """Ensure admin changelist filters do not contain objects excluded via limit_choices_to.""" response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit) self.failUnlessEqual(response.status_code, 200) self.failUnless( - '<div id="changelist-filter">' in response.content, + '<div id="changelist-filter">' in response.content, "Expected filter not found in changelist view." ) self.failIf( '<a href="?color__id__exact=3">Blue</a>' in response.content, "Changelist filter not correctly limited by limit_choices_to." ) - + def testIncorrectLookupParameters(self): """Ensure incorrect lookup parameters are handled gracefully.""" response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'notarealfield': '5'}) - self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) + self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'}) self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) @@ -186,7 +193,7 @@ class CustomModelAdminTest(AdminViewBasicTest): request = self.client.get('/test_admin/admin2/') self.assertTemplateUsed(request, 'custom_admin/index.html') self.assert_('Hello from a custom index template *bar*' in request.content) - + def testCustomAdminSiteView(self): self.client.login(username='super', password='secret') response = self.client.get('/test_admin/%s/my_view/' % self.urlbit) @@ -411,7 +418,7 @@ class AdminViewPermissionsTest(TestCase): post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) self.assertRedirects(post, '/test_admin/admin/admin_views/article/') self.failUnlessEqual(Article.objects.get(pk=1).content, '<p>edited article</p>') - + # one error in form should produce singular error message, multiple errors plural change_dict['title'] = '' post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) @@ -422,7 +429,7 @@ class AdminViewPermissionsTest(TestCase): post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) self.failUnlessEqual(request.status_code, 200) self.failUnless('Please correct the errors below.' in post.content, - 'Plural error message not found in response to post with multiple errors.') + 'Plural error message not found in response to post with multiple errors.') self.client.get('/test_admin/admin/logout/') def testCustomModelAdminTemplates(self): @@ -523,7 +530,7 @@ class AdminViewStringPrimaryKeyTest(TestCase): response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/delete/' % quote(self.pk)) should_contain = """<a href="../../%s/">%s</a>""" % (quote(self.pk), escape(self.pk)) self.assertContains(response, should_contain) - + def test_url_conflicts_with_add(self): "A model with a primary key that ends with add should be visible" add_model = ModelWithStringPrimaryKey(id="i have something to add") @@ -531,7 +538,7 @@ class AdminViewStringPrimaryKeyTest(TestCase): response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(add_model.pk)) should_contain = """<h1>Change model with string primary key</h1>""" self.assertContains(response, should_contain) - + def test_url_conflicts_with_delete(self): "A model with a primary key that ends with delete should be visible" delete_model = ModelWithStringPrimaryKey(id="delete") @@ -539,7 +546,7 @@ class AdminViewStringPrimaryKeyTest(TestCase): response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(delete_model.pk)) should_contain = """<h1>Change model with string primary key</h1>""" self.assertContains(response, should_contain) - + def test_url_conflicts_with_history(self): "A model with a primary key that ends with history should be visible" history_model = ModelWithStringPrimaryKey(id="history") @@ -547,7 +554,7 @@ class AdminViewStringPrimaryKeyTest(TestCase): response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(history_model.pk)) should_contain = """<h1>Change model with string primary key</h1>""" self.assertContains(response, should_contain) - + class SecureViewTest(TestCase): fixtures = ['admin-views-users.xml'] @@ -582,28 +589,28 @@ class SecureViewTest(TestCase): LOGIN_FORM_KEY: 1, 'username': 'joepublic', 'password': 'secret'} - + def tearDown(self): self.client.logout() - + def test_secure_view_shows_login_if_not_logged_in(self): "Ensure that we see the login form" response = self.client.get('/test_admin/admin/secure-view/' ) self.assertTemplateUsed(response, 'admin/login.html') - + def test_secure_view_login_successfully_redirects_to_original_url(self): request = self.client.get('/test_admin/admin/secure-view/') self.failUnlessEqual(request.status_code, 200) query_string = "the-answer=42" login = self.client.post('/test_admin/admin/secure-view/', self.super_login, QUERY_STRING = query_string ) self.assertRedirects(login, '/test_admin/admin/secure-view/?%s' % query_string) - + def test_staff_member_required_decorator_works_as_per_admin_login(self): """ Make sure only staff members can log in. Successful posts to the login page will redirect to the orignal url. - Unsuccessfull attempts will continue to render the login page with + Unsuccessfull attempts will continue to render the login page with a 200 status code. """ # Super User @@ -721,3 +728,80 @@ class AdminViewUnicodeTest(TestCase): self.failUnlessEqual(response.status_code, 200) response = self.client.post('/test_admin/admin/admin_views/book/1/delete/', delete_dict) self.assertRedirects(response, '/test_admin/admin/admin_views/book/') + +class AdminInheritedInlinesTest(TestCase): + fixtures = ['admin-views-users.xml',] + + def setUp(self): + self.client.login(username='super', password='secret') + + def tearDown(self): + self.client.logout() + + def testInline(self): + "Ensure that inline models which inherit from a common parent are correctly handled by admin." + + foo_user = u"foo username" + bar_user = u"bar username" + + name_re = re.compile('name="(.*?)"') + + # test the add case + response = self.client.get('/test_admin/admin/admin_views/persona/add/') + names = name_re.findall(response.content) + # make sure we have no duplicate HTML names + self.failUnlessEqual(len(names), len(set(names))) + + # test the add case + post_data = { + "name": u"Test Name", + # inline data + "accounts-TOTAL_FORMS": u"1", + "accounts-INITIAL_FORMS": u"0", + "accounts-0-username": foo_user, + "accounts-2-TOTAL_FORMS": u"1", + "accounts-2-INITIAL_FORMS": u"0", + "accounts-2-0-username": bar_user, + } + + response = self.client.post('/test_admin/admin/admin_views/persona/add/', post_data) + self.failUnlessEqual(response.status_code, 302) # redirect somewhere + self.failUnlessEqual(Persona.objects.count(), 1) + self.failUnlessEqual(FooAccount.objects.count(), 1) + self.failUnlessEqual(BarAccount.objects.count(), 1) + self.failUnlessEqual(FooAccount.objects.all()[0].username, foo_user) + self.failUnlessEqual(BarAccount.objects.all()[0].username, bar_user) + self.failUnlessEqual(Persona.objects.all()[0].accounts.count(), 2) + + # test the edit case + + response = self.client.get('/test_admin/admin/admin_views/persona/1/') + names = name_re.findall(response.content) + # make sure we have no duplicate HTML names + self.failUnlessEqual(len(names), len(set(names))) + + post_data = { + "name": u"Test Name", + + "accounts-TOTAL_FORMS": "2", + "accounts-INITIAL_FORMS": u"1", + + "accounts-0-username": "%s-1" % foo_user, + "accounts-0-account_ptr": "1", + "accounts-0-persona": "1", + + "accounts-2-TOTAL_FORMS": u"2", + "accounts-2-INITIAL_FORMS": u"1", + + "accounts-2-0-username": "%s-1" % bar_user, + "accounts-2-0-account_ptr": "2", + "accounts-2-0-persona": "1", + } + response = self.client.post('/test_admin/admin/admin_views/persona/1/', post_data) + self.failUnlessEqual(response.status_code, 302) + self.failUnlessEqual(Persona.objects.count(), 1) + self.failUnlessEqual(FooAccount.objects.count(), 1) + self.failUnlessEqual(BarAccount.objects.count(), 1) + self.failUnlessEqual(FooAccount.objects.all()[0].username, "%s-1" % foo_user) + self.failUnlessEqual(BarAccount.objects.all()[0].username, "%s-1" % bar_user) + self.failUnlessEqual(Persona.objects.all()[0].accounts.count(), 2) |
