# coding: utf-8 import re import datetime from django.test import TestCase from django.contrib.auth.models import User, Permission from django.contrib.contenttypes.models import ContentType from django.contrib.admin.models import LogEntry from django.contrib.admin.sites import LOGIN_FORM_KEY from django.contrib.admin.util import quote from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.utils.html import escape # local test models from models import Article, CustomArticle, Section, ModelWithStringPrimaryKey, Person, Persona, FooAccount, BarAccount, Subscriber, ExternalSubscriber, Podcast, EmptyModel try: set except NameError: from sets import Set as set class AdminViewBasicTest(TestCase): fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml'] # 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. """ request = self.client.get('/test_admin/%s/admin_views/article/add' % self.urlbit) 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, "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. """ post_data = { "name": u"Another Section", # inline data "article_set-TOTAL_FORMS": u"3", "article_set-INITIAL_FORMS": u"0", } response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere # Post data for edit inline inline_post_data = { "name": u"Test section", # inline data "article_set-TOTAL_FORMS": u"6", "article_set-INITIAL_FORMS": u"3", "article_set-0-id": u"1", # there is no title in database, give one here or formset will fail. "article_set-0-title": u"Norske bostaver æøå skaper problemer", "article_set-0-content": u"<p>Middle content</p>", "article_set-0-date_0": u"2008-03-18", "article_set-0-date_1": u"11:54:58", "article_set-0-section": u"1", "article_set-1-id": u"2", "article_set-1-title": u"Need a title.", "article_set-1-content": u"<p>Oldest content</p>", "article_set-1-date_0": u"2000-03-18", "article_set-1-date_1": u"11:54:58", "article_set-2-id": u"3", "article_set-2-title": u"Need a title.", "article_set-2-content": u"<p>Newest content</p>", "article_set-2-date_0": u"2009-03-18", "article_set-2-date_1": u"11:54:58", "article_set-3-id": u"", "article_set-3-title": u"", "article_set-3-content": u"", "article_set-3-date_0": u"", "article_set-3-date_1": u"", "article_set-4-id": u"", "article_set-4-title": u"", "article_set-4-content": u"", "article_set-4-date_0": u"", "article_set-4-date_1": u"", "article_set-5-id": u"", "article_set-5-title": u"", "article_set-5-content": u"", "article_set-5-date_0": u"", "article_set-5-date_1": u"", } def testBasicEditPost(self): """ A smoke test to ensure POST on edit_view works. """ response = self.client.post('/test_admin/%s/admin_views/section/1/' % self.urlbit, self.inline_post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere def testEditSaveAs(self): """ Test "save as". """ post_data = self.inline_post_data.copy() post_data.update({ '_saveasnew': u'Save+as+new', "article_set-1-section": u"1", "article_set-2-section": u"1", "article_set-3-section": u"1", "article_set-4-section": u"1", "article_set-5-section": u"1", }) response = self.client.post('/test_admin/%s/admin_views/section/1/' % self.urlbit, post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere def testChangeListSortingCallable(self): """ 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}) self.failUnlessEqual(response.status_code, 200) self.failUnless( 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 callable are out of order." ) def testChangeListSortingModel(self): """ 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}) self.failUnlessEqual(response.status_code, 200) self.failUnless( response.content.index('Newest content') < response.content.index('Middle content') and 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 (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('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( '
great article
', 'date_0': '2008-03-18', 'date_1': '10:54:39', 'section': 1} # Change User should not have access to add articles self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.changeuser_login) # make sure the view removes test cookie self.failUnlessEqual(self.client.session.test_cookie_worked(), False) request = self.client.get('/test_admin/admin/admin_views/article/add/') self.failUnlessEqual(request.status_code, 403) # Try POST just to make sure post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.failUnlessEqual(post.status_code, 403) self.failUnlessEqual(Article.objects.all().count(), 3) self.client.get('/test_admin/admin/logout/') # Add user may login and POST to add view, then redirect to admin root self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) addpage = self.client.get('/test_admin/admin/admin_views/article/add/') self.failUnlessEqual(addpage.status_code, 200) change_list_link = 'Articles ›' self.failIf(change_list_link in addpage.content, 'User restricted to add permission is given link to change list view in breadcrumbs.') post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertRedirects(post, '/test_admin/admin/') self.failUnlessEqual(Article.objects.all().count(), 4) self.client.get('/test_admin/admin/logout/') # Super can add too, but is redirected to the change list view self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login) addpage = self.client.get('/test_admin/admin/admin_views/article/add/') self.failUnlessEqual(addpage.status_code, 200) self.failIf(change_list_link not in addpage.content, 'Unrestricted user is not given link to change list view in breadcrumbs.') post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertRedirects(post, '/test_admin/admin/admin_views/article/') self.failUnlessEqual(Article.objects.all().count(), 5) self.client.get('/test_admin/admin/logout/') # 8509 - if a normal user is already logged in, it is possible # to change user into the superuser without error login = self.client.login(username='joepublic', password='secret') # Check and make sure that if user expires, data still persists self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login) # make sure the view removes test cookie self.failUnlessEqual(self.client.session.test_cookie_worked(), False) def testChangeView(self): """Change view should restrict access and allow users to edit items.""" change_dict = {'title' : 'Ikke fordømt', 'content': 'edited article
', 'date_0': '2008-03-18', 'date_1': '10:54:39', 'section': 1} # add user shoud not be able to view the list of article or change any of them self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) request = self.client.get('/test_admin/admin/admin_views/article/') self.failUnlessEqual(request.status_code, 403) request = self.client.get('/test_admin/admin/admin_views/article/1/') self.failUnlessEqual(request.status_code, 403) post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) self.failUnlessEqual(post.status_code, 403) self.client.get('/test_admin/admin/logout/') # change user can view all items and edit them self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.changeuser_login) request = self.client.get('/test_admin/admin/admin_views/article/') self.failUnlessEqual(request.status_code, 200) request = self.client.get('/test_admin/admin/admin_views/article/1/') self.failUnlessEqual(request.status_code, 200) 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, 'edited article
') # 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) self.failUnlessEqual(request.status_code, 200) self.failUnless('Please correct the error below.' in post.content, 'Singular error message not found in response to post with one error.') change_dict['content'] = '' 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.') self.client.get('/test_admin/admin/logout/') def testCustomModelAdminTemplates(self): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login) # Test custom change list template with custom extra context request = self.client.get('/test_admin/admin/admin_views/customarticle/') self.failUnlessEqual(request.status_code, 200) self.assert_("var hello = 'Hello!';" in request.content) self.assertTemplateUsed(request, 'custom_admin/change_list.html') # Test custom change form template request = self.client.get('/test_admin/admin/admin_views/customarticle/add/') self.assertTemplateUsed(request, 'custom_admin/change_form.html') # Add an article so we can test delete and history views post = self.client.post('/test_admin/admin/admin_views/customarticle/add/', { 'content': 'great article
', 'date_0': '2008-03-18', 'date_1': '10:54:39' }) self.assertRedirects(post, '/test_admin/admin/admin_views/customarticle/') self.failUnlessEqual(CustomArticle.objects.all().count(), 1) # Test custom delete and object history templates request = self.client.get('/test_admin/admin/admin_views/customarticle/1/delete/') self.assertTemplateUsed(request, 'custom_admin/delete_confirmation.html') request = self.client.get('/test_admin/admin/admin_views/customarticle/1/history/') self.assertTemplateUsed(request, 'custom_admin/object_history.html') self.client.get('/test_admin/admin/logout/') def testDeleteView(self): """Delete view should restrict access and actually delete items.""" delete_dict = {'post': 'yes'} # add user shoud not be able to delete articles self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) request = self.client.get('/test_admin/admin/admin_views/article/1/delete/') self.failUnlessEqual(request.status_code, 403) post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict) self.failUnlessEqual(post.status_code, 403) self.failUnlessEqual(Article.objects.all().count(), 3) self.client.get('/test_admin/admin/logout/') # Delete user can delete self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.deleteuser_login) response = self.client.get('/test_admin/admin/admin_views/section/1/delete/') # test response contains link to related Article self.assertContains(response, "admin_views/article/1/") response = self.client.get('/test_admin/admin/admin_views/article/1/delete/') self.failUnlessEqual(response.status_code, 200) post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict) self.assertRedirects(post, '/test_admin/admin/') self.failUnlessEqual(Article.objects.all().count(), 2) self.client.get('/test_admin/admin/logout/') class AdminViewStringPrimaryKeyTest(TestCase): fixtures = ['admin-views-users.xml', 'string-primary-key.xml'] def __init__(self, *args): super(AdminViewStringPrimaryKeyTest, self).__init__(*args) self.pk = """abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`""" def setUp(self): self.client.login(username='super', password='secret') content_type_pk = ContentType.objects.get_for_model(ModelWithStringPrimaryKey).pk LogEntry.objects.log_action(100, content_type_pk, self.pk, self.pk, 2, change_message='') def tearDown(self): self.client.logout() def test_get_change_view(self): "Retrieving the object using urlencoded form of primary key should work" response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(self.pk)) self.assertContains(response, escape(self.pk)) self.failUnlessEqual(response.status_code, 200) def test_changelist_to_changeform_link(self): "The link from the changelist referring to the changeform of the object should be quoted" response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/') should_contain = """