From a6e7755a55edcebfe2250538ce1132fa07a85dc3 Mon Sep 17 00:00:00 2001 From: montiniz Date: Wed, 26 Nov 2014 09:42:17 -0600 Subject: [PATCH] Fixes for compatibility with python3. --- djadmin2/apiviews.py | 2 +- example/blog/models.py | 11 ++- example/blog/tests/test_apiviews.py | 6 +- example/blog/tests/test_modelforms.py | 20 +++-- example/blog/tests/test_views.py | 52 +++++++++---- example/files/models.py | 15 ++-- example/files/tests/test_views.py | 106 ++++++++++++++++++-------- example/polls/models.py | 10 ++- example/polls/tests/test_views.py | 61 ++++++++++----- 9 files changed, 198 insertions(+), 85 deletions(-) diff --git a/djadmin2/apiviews.py b/djadmin2/apiviews.py index c733c32..39e7e32 100644 --- a/djadmin2/apiviews.py +++ b/djadmin2/apiviews.py @@ -18,7 +18,7 @@ class Admin2APISerializer(serializers.HyperlinkedModelSerializer): _default_view_name = 'admin2:%(app_label)s_%(model_name)s_api_detail' pk = fields.Field(source='pk') - __str__ = fields.Field(source='__unicode__') + __unicode__ = fields.Field(source='__str__') class Admin2APIMixin(Admin2Mixin): diff --git a/example/blog/models.py b/example/blog/models.py index e3503f0..ff2ebb6 100644 --- a/example/blog/models.py +++ b/example/blog/models.py @@ -7,13 +7,14 @@ from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=255, verbose_name=_('title')) body = models.TextField(verbose_name=_('body')) published = models.BooleanField(default=False, verbose_name=_('published')) published_date = models.DateField(blank=True, null=True) - def __unicode__(self): + def __str__(self): return self.title class Meta: @@ -21,11 +22,13 @@ class Post(models.Model): verbose_name_plural = _('posts') +@python_2_unicode_compatible class Comment(models.Model): - post = models.ForeignKey(Post, verbose_name=_('post'), related_name="comments") + post = models.ForeignKey( + Post, verbose_name=_('post'), related_name="comments") body = models.TextField(verbose_name=_('body')) - def __unicode__(self): + def __str__(self): return self.body class Meta: @@ -33,7 +36,7 @@ class Comment(models.Model): verbose_name_plural = _('comments') -#### Models needed for testing NestedObjects +# Models needed for testing NestedObjects @python_2_unicode_compatible class Count(models.Model): diff --git a/example/blog/tests/test_apiviews.py b/example/blog/tests/test_apiviews.py index 52bdbbe..aca0e8d 100644 --- a/example/blog/tests/test_apiviews.py +++ b/example/blog/tests/test_apiviews.py @@ -1,8 +1,10 @@ +from __future__ import unicode_literals from django.contrib.auth.models import AnonymousUser, User from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse from django.test import TestCase from django.test.client import RequestFactory +from django.utils.encoding import force_text import json @@ -69,7 +71,7 @@ class ListCreateAPIViewTest(APITestCase): response.render() self.assertEqual(response.status_code, 200) - self.assertIn('"__str__": "Foo"', response.content) + self.assertIn('"__unicode__": "Foo"', force_text(response.content)) def test_pagination(self): request = self.factory.get(reverse('admin2:blog_post_api_list')) @@ -81,7 +83,7 @@ class ListCreateAPIViewTest(APITestCase): response.render() self.assertEqual(response.status_code, 200) - data = json.loads(response.content) + data = json.loads(force_text(response.content)) self.assertEqual(data['count'], 0) # next and previous fields exist, but are null because we have no # content diff --git a/example/blog/tests/test_modelforms.py b/example/blog/tests/test_modelforms.py index ad39eee..6845d47 100644 --- a/example/blog/tests/test_modelforms.py +++ b/example/blog/tests/test_modelforms.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django import forms from django.test import TestCase @@ -8,6 +10,7 @@ from ..models import Post class ModelFormFactoryTest(TestCase): + def test_modelform_factory(self): form_class = modelform_factory(Post) self.assertTrue(form_class) @@ -16,8 +19,9 @@ class ModelFormFactoryTest(TestCase): class GetFloppyformWidgetTest(TestCase): + def assertExpectWidget(self, instance, new_class_, - equal_attributes=None, new_attributes=None): + equal_attributes=None, new_attributes=None): new_instance = floppify_widget(instance) self.assertEqual(new_instance.__class__, new_class_) if equal_attributes: @@ -33,8 +37,8 @@ class GetFloppyformWidgetTest(TestCase): old_attr = getattr(instance, attribute) new_attr = getattr(new_instance, attribute) self.assertEqual(old_attr, new_attr, - 'Original widget\'s attribute was not copied: %r != %r' % - (old_attr, new_attr)) + 'Original widget\'s attribute was not copied: %r != %r' % + (old_attr, new_attr)) if new_attributes: for attribute, value in new_attributes.items(): self.assertTrue( @@ -43,8 +47,8 @@ class GetFloppyformWidgetTest(TestCase): 'generated widget %r' % (attribute, new_instance)) new_attr = getattr(new_instance, attribute) self.assertEqual(new_attr, value, - 'Generated widget\'s attribute is not as expected: ' - '%r != %r' % (new_attr, value)) + 'Generated widget\'s attribute is not as expected: ' + '%r != %r' % (new_attr, value)) def test_created_widget_doesnt_leak_attributes_into_original_widget(self): widget = forms.TextInput() @@ -146,7 +150,7 @@ class GetFloppyformWidgetTest(TestCase): widget, floppyforms.widgets.ClearableFileInput, ['initial_text', 'input_text', 'clear_checkbox_label', - 'template_with_initial', 'template_with_clear']) + 'template_with_initial', 'template_with_clear']) def test_textarea_widget(self): self.assertExpectWidget( @@ -222,7 +226,7 @@ class GetFloppyformWidgetTest(TestCase): forms.widgets.NullBooleanSelect(), floppyforms.widgets.NullBooleanSelect, ('choices', 'allow_multiple_selected',)) - + widget = forms.widgets.NullBooleanSelect() widget.choices = list(widget.choices) @@ -349,6 +353,7 @@ class GetFloppyformWidgetTest(TestCase): class ModelFormTest(TestCase): + def test_custom_base_form(self): class MyForm(forms.ModelForm): pass @@ -410,6 +415,7 @@ class ModelFormTest(TestCase): class FieldWidgetTest(TestCase): + def test_dont_overwrite_none_default_widget(self): # we don't create the floppyform EmailInput for the email field here # since we have overwritten the default widget. However we replace the diff --git a/example/blog/tests/test_views.py b/example/blog/tests/test_views.py index 9d31b97..3301908 100644 --- a/example/blog/tests/test_views.py +++ b/example/blog/tests/test_views.py @@ -1,18 +1,22 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals from datetime import datetime from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.core.urlresolvers import reverse from django.test import TestCase, Client +from django.utils.encoding import force_text from ..models import Post, Comment class BaseIntegrationTest(TestCase): + """ Base TestCase for integration tests. """ + def setUp(self): self.client = Client() self.user = get_user_model()(username='user', is_staff=True, @@ -23,12 +27,14 @@ class BaseIntegrationTest(TestCase): class AdminIndexTest(BaseIntegrationTest): + def test_view_ok(self): response = self.client.get(reverse("admin2:dashboard")) self.assertContains(response, reverse("admin2:blog_post_index")) class UserListTest(BaseIntegrationTest): + def test_search_users_m2m_group(self): # This test should cause the distinct search path to exectue group = Group.objects.create(name="Test Group") @@ -40,6 +46,7 @@ class UserListTest(BaseIntegrationTest): class CommentListTest(BaseIntegrationTest): + def test_search_comments(self): # Test search across Foriegn Keys post_1 = Post.objects.create(title="post_1_title", body="body") @@ -49,7 +56,8 @@ class CommentListTest(BaseIntegrationTest): Comment.objects.create(body="comment_post_2", post=post_2) params = {"q": "post_1_title"} - response = self.client.get(reverse("admin2:blog_comment_index"), params) + response = self.client.get( + reverse("admin2:blog_comment_index"), params) self.assertContains(response, "comment_post_1_a") self.assertContains(response, "comment_post_1_b") self.assertNotContains(response, "comment_post_2") @@ -62,6 +70,7 @@ class CommentListTest(BaseIntegrationTest): class PostListTest(BaseIntegrationTest): + def _create_posts(self): Post.objects.bulk_create([ Post( @@ -129,7 +138,8 @@ class PostListTest(BaseIntegrationTest): def test_actions_displayed(self): response = self.client.get(reverse("admin2:blog_post_index")) - self.assertInHTML('Delete selected items', response.content) + self.assertInHTML( + 'Delete selected items', force_text(response.content)) def test_actions_displayed_twice(self): # If actions_on_top and actions_on_bottom are both set @@ -139,27 +149,33 @@ class PostListTest(BaseIntegrationTest): def test_delete_selected_post(self): post = Post.objects.create(title="A Post Title", body="body") - params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(post.pk)} + params = {'action': 'DeleteSelectedAction', + 'selected_model_pk': str(post.pk)} response = self.client.post(reverse("admin2:blog_post_index"), params) # caution : uses pluralization - self.assertInHTML('

Are you sure you want to delete the selected post? The following item will be deleted:

', response.content) + self.assertInHTML( + '

Are you sure you want to delete the selected post? The following item will be deleted:

', force_text(response.content)) def test_delete_selected_post_confirmation(self): post = Post.objects.create(title="A Post Title", body="body") - params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(post.pk), 'confirmed': 'yes'} + params = {'action': 'DeleteSelectedAction', + 'selected_model_pk': str(post.pk), 'confirmed': 'yes'} response = self.client.post(reverse("admin2:blog_post_index"), params) self.assertRedirects(response, reverse("admin2:blog_post_index")) def test_delete_selected_post_none_selected(self): Post.objects.create(title="A Post Title", body="body") params = {'action': 'DeleteSelectedAction'} - response = self.client.post(reverse("admin2:blog_post_index"), params, follow=True) - self.assertContains(response, "Items must be selected in order to perform actions on them. No items have been changed.") + response = self.client.post( + reverse("admin2:blog_post_index"), params, follow=True) + self.assertContains( + response, "Items must be selected in order to perform actions on them. No items have been changed.") def test_search_posts(self): Post.objects.create(title="A Post Title", body="body") Post.objects.create(title="Another Post Title", body="body") - Post.objects.create(title="Post With Keyword In Body", body="another post body") + Post.objects.create( + title="Post With Keyword In Body", body="another post body") params = {"q": "another"} response = self.client.get(reverse("admin2:blog_post_index"), params) self.assertContains(response, "Another Post Title") @@ -167,12 +183,14 @@ class PostListTest(BaseIntegrationTest): self.assertNotContains(response, "A Post Title") def test_renderer_title(self): - Post.objects.create(title='a lowercase title', body='body', published=False) + Post.objects.create( + title='a lowercase title', body='body', published=False) response = self.client.get(reverse('admin2:blog_post_index')) self.assertContains(response, 'A Lowercase Title') def test_renderer_body(self): - Post.objects.create(title='title', body='a lowercase body', published=False) + Post.objects.create( + title='title', body='a lowercase body', published=False) response = self.client.get(reverse('admin2:blog_post_index')) self.assertContains(response, 'a lowercase body') @@ -311,7 +329,8 @@ class PostListTestCustomAction(BaseIntegrationTest): def test_publish_action_displayed_in_list(self): response = self.client.get(reverse("admin2:blog_post_index")) - self.assertInHTML('Publish selected items', response.content) + self.assertInHTML( + 'Publish selected items', force_text(response.content)) def test_publish_selected_items(self): post = Post.objects.create(title="A Post Title", @@ -329,7 +348,8 @@ class PostListTestCustomAction(BaseIntegrationTest): def test_unpublish_action_displayed_in_list(self): response = self.client.get(reverse("admin2:blog_post_index")) - self.assertInHTML('Unpublish selected items', response.content) + self.assertInHTML( + 'Unpublish selected items', force_text(response.content)) def test_unpublish_selected_items(self): post = Post.objects.create(title="A Post Title", @@ -346,6 +366,7 @@ class PostListTestCustomAction(BaseIntegrationTest): class PostDetailViewTest(BaseIntegrationTest): + def test_view_ok(self): post = Post.objects.create(title="A Post Title", body="body") response = self.client.get(reverse("admin2:blog_post_detail", @@ -354,9 +375,11 @@ class PostDetailViewTest(BaseIntegrationTest): class PostCreateViewTest(BaseIntegrationTest): + def test_view_ok(self): response = self.client.get(reverse("admin2:blog_post_create")) - self.assertNotIn('''enctype="multipart/form-data"''', response.content) + self.assertNotIn( + '''enctype="multipart/form-data"''', force_text(response.content)) self.assertEqual(response.status_code, 200) def test_create_post(self): @@ -422,6 +445,7 @@ class PostCreateViewTest(BaseIntegrationTest): class PostDeleteViewTest(BaseIntegrationTest): + def test_view_ok(self): post = Post.objects.create(title="A Post Title", body="body") response = self.client.get(reverse("admin2:blog_post_delete", @@ -437,9 +461,11 @@ class PostDeleteViewTest(BaseIntegrationTest): class PostDeleteActionTest(BaseIntegrationTest): + """ Tests the behaviour of the 'Delete selected items' action. """ + def test_confirmation_page(self): p1 = Post.objects.create(title="A Post Title", body="body") p2 = Post.objects.create(title="A Post Title", body="body") diff --git a/example/files/models.py b/example/files/models.py index 701b4fa..b373326 100644 --- a/example/files/models.py +++ b/example/files/models.py @@ -2,14 +2,17 @@ from __future__ import division, absolute_import, unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ +@python_2_unicode_compatible class CaptionedFile(models.Model): caption = models.CharField(max_length=200, verbose_name=_('caption')) - publication = models.FileField(upload_to='media', verbose_name=_('Uploaded File')) + publication = models.FileField( + upload_to='media', verbose_name=_('Uploaded File')) - def __unicode__(self): + def __str__(self): return self.caption class Meta: @@ -17,11 +20,13 @@ class CaptionedFile(models.Model): verbose_name_plural = _('Captioned Files') +@python_2_unicode_compatible class UncaptionedFile(models.Model): - publication = models.FileField(upload_to='media', verbose_name=_('Uploaded File')) + publication = models.FileField( + upload_to='media', verbose_name=_('Uploaded File')) - def __unicode__(self): - return unicode(self.publication) + def __str__(self): + return self.publication class Meta: verbose_name = _('Uncaptioned File') diff --git a/example/files/tests/test_views.py b/example/files/tests/test_views.py index 1c1721f..d36a456 100644 --- a/example/files/tests/test_views.py +++ b/example/files/tests/test_views.py @@ -2,6 +2,7 @@ from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse from django.test import TestCase, Client from django.utils import timezone +from django.utils.encoding import force_text from ..models import CaptionedFile @@ -12,9 +13,11 @@ fixture_file = path.join(fixture_dir, 'pubtest.txt') class BaseIntegrationTest(TestCase): + """ Base TestCase for integration tests. """ + def setUp(self): self.client = Client() self.user = get_user_model()(username='user', is_staff=True, @@ -25,55 +28,77 @@ class BaseIntegrationTest(TestCase): class AdminIndexTest(BaseIntegrationTest): + def test_view_ok(self): response = self.client.get(reverse("admin2:dashboard")) - self.assertContains(response, reverse("admin2:files_captionedfile_index")) + self.assertContains( + response, reverse("admin2:files_captionedfile_index")) class CaptionedFileListTest(BaseIntegrationTest): + def test_view_ok(self): - captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) + captioned_file = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) response = self.client.get(reverse("admin2:files_captionedfile_index")) self.assertContains(response, captioned_file.caption) def test_actions_displayed(self): response = self.client.get(reverse("admin2:files_captionedfile_index")) - self.assertInHTML('Delete selected items', response.content) + self.assertInHTML( + 'Delete selected items', force_text(response.content)) def test_delete_selected_captioned_file(self): - captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) - params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(captioned_file.pk)} - response = self.client.post(reverse("admin2:files_captionedfile_index"), params) - self.assertInHTML('

Are you sure you want to delete the selected Captioned File? The following item will be deleted:

', response.content) + captioned_file = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) + params = {'action': 'DeleteSelectedAction', + 'selected_model_pk': str(captioned_file.pk)} + response = self.client.post( + reverse("admin2:files_captionedfile_index"), params) + self.assertInHTML( + '

Are you sure you want to delete the selected Captioned File? The following item will be deleted:

', force_text(response.content)) def test_delete_selected_captioned_file_confirmation(self): - captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) - params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(captioned_file.pk), 'confirmed': 'yes'} - response = self.client.post(reverse("admin2:files_captionedfile_index"), params) - self.assertRedirects(response, reverse("admin2:files_captionedfile_index")) + captioned_file = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) + params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str( + captioned_file.pk), 'confirmed': 'yes'} + response = self.client.post( + reverse("admin2:files_captionedfile_index"), params) + self.assertRedirects( + response, reverse("admin2:files_captionedfile_index")) def test_delete_selected_captioned_file_none_selected(self): - CaptionedFile.objects.create(caption="some file", publication=fixture_file) + CaptionedFile.objects.create( + caption="some file", publication=fixture_file) params = {'action': 'DeleteSelectedAction'} - response = self.client.post(reverse("admin2:files_captionedfile_index"), params, follow=True) - self.assertContains(response, "Items must be selected in order to perform actions on them. No items have been changed.") + response = self.client.post( + reverse("admin2:files_captionedfile_index"), params, follow=True) + self.assertContains( + response, "Items must be selected in order to perform actions on them. No items have been changed.") class CaptionedFileDetailViewTest(BaseIntegrationTest): + def test_view_ok(self): - captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) - response = self.client.get(reverse("admin2:files_captionedfile_detail", args=(captioned_file.pk, ))) + captioned_file = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) + response = self.client.get( + reverse("admin2:files_captionedfile_detail", args=(captioned_file.pk, ))) self.assertContains(response, captioned_file.caption) class CaptionedFileCreateViewTest(BaseIntegrationTest): + def test_view_ok(self): - response = self.client.get(reverse("admin2:files_captionedfile_create")) - self.assertIn('enctype="multipart/form-data"', response.content) + response = self.client.get( + reverse("admin2:files_captionedfile_create")) + self.assertIn( + 'enctype="multipart/form-data"', force_text(response.content)) self.assertEqual(response.status_code, 200) def test_create_captioned_file(self): - with open(fixture_file, 'r') as fp: + with open(fixture_file, 'rb') as fp: params = { "caption": "some file", "publication": fp, @@ -81,15 +106,17 @@ class CaptionedFileCreateViewTest(BaseIntegrationTest): response = self.client.post(reverse("admin2:files_captionedfile_create"), params, follow=True) - self.assertTrue(CaptionedFile.objects.filter(caption="some file").exists()) - self.assertRedirects(response, reverse("admin2:files_captionedfile_index")) + self.assertTrue( + CaptionedFile.objects.filter(caption="some file").exists()) + self.assertRedirects( + response, reverse("admin2:files_captionedfile_index")) def test_save_and_add_another_redirects_to_create(self): """ Tests that choosing 'Save and add another' from the model create page redirects the user to the model create page. """ - with open(fixture_file, 'r') as fp: + with open(fixture_file, 'rb') as fp: params = { "caption": "some file", "publication": fp, @@ -97,15 +124,17 @@ class CaptionedFileCreateViewTest(BaseIntegrationTest): } response = self.client.post(reverse("admin2:files_captionedfile_create"), params) - self.assertTrue(CaptionedFile.objects.filter(caption="some file").exists()) - self.assertRedirects(response, reverse("admin2:files_captionedfile_create")) + self.assertTrue( + CaptionedFile.objects.filter(caption="some file").exists()) + self.assertRedirects( + response, reverse("admin2:files_captionedfile_create")) def test_save_and_continue_editing_redirects_to_update(self): """ Tests that choosing "Save and continue editing" redirects the user to the model update form. """ - with open(fixture_file, 'r') as fp: + with open(fixture_file, 'rb') as fp: params = { "caption": "some file", "publication": fp, @@ -119,27 +148,36 @@ class CaptionedFileCreateViewTest(BaseIntegrationTest): class CaptionedFileDeleteViewTest(BaseIntegrationTest): + def test_view_ok(self): - captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) + captioned_file = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) response = self.client.get(reverse("admin2:files_captionedfile_delete", args=(captioned_file.pk, ))) self.assertContains(response, captioned_file.caption) def test_delete_captioned_file(self): - captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) + captioned_file = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) response = self.client.post(reverse("admin2:files_captionedfile_delete", args=(captioned_file.pk, ))) - self.assertRedirects(response, reverse("admin2:files_captionedfile_index")) - self.assertFalse(CaptionedFile.objects.filter(pk=captioned_file.pk).exists()) + self.assertRedirects( + response, reverse("admin2:files_captionedfile_index")) + self.assertFalse( + CaptionedFile.objects.filter(pk=captioned_file.pk).exists()) class FileDeleteActionTest(BaseIntegrationTest): + """ Tests the behaviour of the 'Delete selected items' action. """ + def test_confirmation_page(self): - cf1 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) - cf2 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) + cf1 = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) + cf2 = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) params = { 'action': 'DeleteSelectedAction', 'selected_model_pk': [cf1.pk, cf2.pk] @@ -150,8 +188,10 @@ class FileDeleteActionTest(BaseIntegrationTest): self.assertContains(response, cf2.caption) def test_results_page(self): - cf1 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) - cf2 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file) + cf1 = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) + cf2 = CaptionedFile.objects.create( + caption="some file", publication=fixture_file) params = { 'action': 'DeleteSelectedAction', 'selected_model_pk': [cf1.pk, cf2.pk], diff --git a/example/polls/models.py b/example/polls/models.py index 0bb467c..44cc4c2 100644 --- a/example/polls/models.py +++ b/example/polls/models.py @@ -3,16 +3,18 @@ from __future__ import division, absolute_import, unicode_literals import datetime +from django.utils.encoding import python_2_unicode_compatible from django.db import models from django.utils import timezone from django.utils.translation import ugettext_lazy as _ +@python_2_unicode_compatible class Poll(models.Model): question = models.CharField(max_length=200, verbose_name=_('question')) pub_date = models.DateTimeField(verbose_name=_('date published')) - def __unicode__(self): + def __str__(self): return self.question def was_published_recently(self): @@ -26,12 +28,14 @@ class Poll(models.Model): verbose_name_plural = _('polls') +@python_2_unicode_compatible class Choice(models.Model): poll = models.ForeignKey(Poll, verbose_name=_('poll')) - choice_text = models.CharField(max_length=200, verbose_name=_('choice text')) + choice_text = models.CharField( + max_length=200, verbose_name=_('choice text')) votes = models.IntegerField(default=0, verbose_name=_('votes')) - def __unicode__(self): + def __str__(self): return self.choice_text class Meta: diff --git a/example/polls/tests/test_views.py b/example/polls/tests/test_views.py index c212a3f..67ab56a 100644 --- a/example/polls/tests/test_views.py +++ b/example/polls/tests/test_views.py @@ -2,14 +2,17 @@ from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse from django.test import TestCase, Client from django.utils import timezone +from django.utils.encoding import force_text from ..models import Poll class BaseIntegrationTest(TestCase): + """ Base TestCase for integration tests. """ + def setUp(self): self.client = Client() self.user = get_user_model()(username='user', is_staff=True, @@ -20,48 +23,63 @@ class BaseIntegrationTest(TestCase): class AdminIndexTest(BaseIntegrationTest): + def test_view_ok(self): response = self.client.get(reverse("admin2:dashboard")) self.assertContains(response, reverse("admin2:polls_poll_index")) class PollListTest(BaseIntegrationTest): + def test_view_ok(self): - poll = Poll.objects.create(question="some question", pub_date=timezone.now()) + poll = Poll.objects.create( + question="some question", pub_date=timezone.now()) response = self.client.get(reverse("admin2:polls_poll_index")) self.assertContains(response, poll.question) def test_actions_displayed(self): response = self.client.get(reverse("admin2:polls_poll_index")) - self.assertInHTML('Delete selected items', response.content) + self.assertInHTML( + 'Delete selected items', force_text(response.content)) def test_delete_selected_poll(self): - poll = Poll.objects.create(question="some question", pub_date=timezone.now()) - params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(poll.pk)} + poll = Poll.objects.create( + question="some question", pub_date=timezone.now()) + params = {'action': 'DeleteSelectedAction', + 'selected_model_pk': str(poll.pk)} response = self.client.post(reverse("admin2:polls_poll_index"), params) - self.assertInHTML('

Are you sure you want to delete the selected poll? The following item will be deleted:

', response.content) + self.assertInHTML( + '

Are you sure you want to delete the selected poll? The following item will be deleted:

', force_text(response.content)) def test_delete_selected_poll_confirmation(self): - poll = Poll.objects.create(question="some question", pub_date=timezone.now()) - params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(poll.pk), 'confirmed': 'yes'} + poll = Poll.objects.create( + question="some question", pub_date=timezone.now()) + params = {'action': 'DeleteSelectedAction', + 'selected_model_pk': str(poll.pk), 'confirmed': 'yes'} response = self.client.post(reverse("admin2:polls_poll_index"), params) self.assertRedirects(response, reverse("admin2:polls_poll_index")) def test_delete_selected_poll_none_selected(self): Poll.objects.create(question="some question", pub_date=timezone.now()) params = {'action': 'DeleteSelectedAction'} - response = self.client.post(reverse("admin2:polls_poll_index"), params, follow=True) - self.assertContains(response, "Items must be selected in order to perform actions on them. No items have been changed.") + response = self.client.post( + reverse("admin2:polls_poll_index"), params, follow=True) + self.assertContains( + response, "Items must be selected in order to perform actions on them. No items have been changed.") class PollDetailViewTest(BaseIntegrationTest): + def test_view_ok(self): - poll = Poll.objects.create(question="some question", pub_date=timezone.now()) - response = self.client.get(reverse("admin2:polls_poll_detail", args=(poll.pk, ))) + poll = Poll.objects.create( + question="some question", pub_date=timezone.now()) + response = self.client.get( + reverse("admin2:polls_poll_detail", args=(poll.pk, ))) self.assertContains(response, poll.question) class PollCreateViewTest(BaseIntegrationTest): + def test_view_ok(self): response = self.client.get(reverse("admin2:polls_poll_create")) self.assertEqual(response.status_code, 200) @@ -119,14 +137,17 @@ class PollCreateViewTest(BaseIntegrationTest): class PollDeleteViewTest(BaseIntegrationTest): + def test_view_ok(self): - poll = Poll.objects.create(question="some question", pub_date=timezone.now()) + poll = Poll.objects.create( + question="some question", pub_date=timezone.now()) response = self.client.get(reverse("admin2:polls_poll_delete", args=(poll.pk, ))) self.assertContains(response, poll.question) def test_delete_poll(self): - poll = Poll.objects.create(question="some question", pub_date=timezone.now()) + poll = Poll.objects.create( + question="some question", pub_date=timezone.now()) response = self.client.post(reverse("admin2:polls_poll_delete", args=(poll.pk, ))) self.assertRedirects(response, reverse("admin2:polls_poll_index")) @@ -134,12 +155,16 @@ class PollDeleteViewTest(BaseIntegrationTest): class PollDeleteActionTest(BaseIntegrationTest): + """ Tests the behaviour of the 'Delete selected items' action. """ + def test_confirmation_page(self): - p1 = Poll.objects.create(question="some question", pub_date=timezone.now()) - p2 = Poll.objects.create(question="some question", pub_date=timezone.now()) + p1 = Poll.objects.create( + question="some question", pub_date=timezone.now()) + p2 = Poll.objects.create( + question="some question", pub_date=timezone.now()) params = { 'action': 'DeleteSelectedAction', 'selected_model_pk': [p1.pk, p2.pk] @@ -150,8 +175,10 @@ class PollDeleteActionTest(BaseIntegrationTest): self.assertContains(response, p2.question) def test_results_page(self): - p1 = Poll.objects.create(question="some question", pub_date=timezone.now()) - p2 = Poll.objects.create(question="some question", pub_date=timezone.now()) + p1 = Poll.objects.create( + question="some question", pub_date=timezone.now()) + p2 = Poll.objects.create( + question="some question", pub_date=timezone.now()) params = { 'action': 'DeleteSelectedAction', 'selected_model_pk': [p1.pk, p2.pk],