Browse Source

Fix the rest of tests for Python 3.5

NeKit 9 years ago
parent
commit
b7794363d7
40 changed files with 495 additions and 531 deletions
  1. 5 5
      misago/conf/tests/test_admin_views.py
  2. 1 1
      misago/conf/tests/test_context_processors.py
  3. 4 4
      misago/core/tests/test_apipaginator.py
  4. 2 4
      misago/core/tests/test_decorators.py
  5. 11 18
      misago/core/tests/test_errorpages.py
  6. 2 1
      misago/core/tests/test_serializer.py
  7. 3 2
      misago/core/tests/test_setup.py
  8. 3 3
      misago/core/tests/test_shortcuts.py
  9. 2 3
      misago/core/tests/test_views.py
  10. 4 4
      misago/legal/tests.py
  11. 5 4
      misago/threads/tests/test_subscriptions.py
  12. 3 2
      misago/threads/tests/test_synchronizethreads.py
  13. 18 16
      misago/threads/tests/test_thread_patch_api.py
  14. 4 2
      misago/threads/tests/test_threads_api.py
  15. 21 20
      misago/threads/tests/test_threads_merge_api.py
  16. 179 155
      misago/threads/tests/test_threadslists.py
  17. 2 3
      misago/users/tests/test_activation_views.py
  18. 24 39
      misago/users/tests/test_auth_api.py
  19. 4 4
      misago/users/tests/test_auth_views.py
  20. 6 6
      misago/users/tests/test_banadmin_views.py
  21. 2 1
      misago/users/tests/test_bansmaintenance.py
  22. 2 1
      misago/users/tests/test_captcha_api.py
  23. 1 2
      misago/users/tests/test_decorators.py
  24. 4 4
      misago/users/tests/test_djangoadmin_auth.py
  25. 4 8
      misago/users/tests/test_forgottenpassword_views.py
  26. 2 1
      misago/users/tests/test_lists_views.py
  27. 4 8
      misago/users/tests/test_options_views.py
  28. 16 15
      misago/users/tests/test_profile_views.py
  29. 11 12
      misago/users/tests/test_rankadmin_views.py
  30. 7 7
      misago/users/tests/test_testutils.py
  31. 24 36
      misago/users/tests/test_user_avatar_api.py
  32. 4 8
      misago/users/tests/test_user_changeemail_api.py
  33. 3 7
      misago/users/tests/test_user_changepassword_api.py
  34. 11 15
      misago/users/tests/test_user_create_api.py
  35. 7 9
      misago/users/tests/test_user_signature_api.py
  36. 23 27
      misago/users/tests/test_user_username_api.py
  37. 26 24
      misago/users/tests/test_useradmin_views.py
  38. 5 7
      misago/users/tests/test_usernamechanges_api.py
  39. 29 35
      misago/users/tests/test_users_api.py
  40. 7 8
      misago/users/tests/test_warningadmin_views.py

+ 5 - 5
misago/conf/tests/test_admin_views.py

@@ -10,7 +10,7 @@ class AdminSettingsViewsTests(AdminTestCase):
         """admin index view contains settings link"""
         response = self.client.get(reverse('misago:admin:index'))
 
-        self.assertIn(reverse('misago:admin:settings:index'), response.content)
+        self.assertContains(response, reverse('misago:admin:settings:index'))
 
     def test_groups_list_view(self):
         """settings group view returns 200 and contains all settings groups"""
@@ -21,8 +21,8 @@ class AdminSettingsViewsTests(AdminTestCase):
             group_link = reverse('misago:admin:settings:group', kwargs={
                 'key': group.key
             })
-            self.assertIn(group.name, response.content)
-            self.assertIn(group_link, response.content)
+            self.assertContains(response, group.name)
+            self.assertContains(response, group_link)
 
     def test_groups_views(self):
         """
@@ -35,12 +35,12 @@ class AdminSettingsViewsTests(AdminTestCase):
             response = self.client.get(group_link)
 
             self.assertEqual(response.status_code, 200)
-            self.assertIn(group.name, response.content)
+            self.assertContains(response, group.name)
 
             values = {}
             for setting in group.setting_set.all():
                 values[setting.setting] = setting.value
-                self.assertIn(setting.name, response.content)
+                self.assertContains(response, setting.name)
 
             response = self.client.post(group_link, data=values)
             self.assertEqual(response.status_code, 302)

+ 1 - 1
misago/conf/tests/test_context_processors.py

@@ -25,4 +25,4 @@ class ContextProcessorsTests(TestCase):
         """site configuration is preloaded by middleware"""
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn('"SETTINGS": {"', response.content)
+        self.assertContains(response, '"SETTINGS": {"')

+ 4 - 4
misago/core/tests/test_apipaginator.py

@@ -1,5 +1,5 @@
 from django.test import TestCase
-
+from django.utils.six.moves import range
 from ..apipaginator import ApiPaginator
 
 
@@ -36,7 +36,7 @@ class PaginatorTests(TestCase):
     def test_first_page(self):
         """pagination works for first page of queryset"""
         paginator = ApiPaginator(6, 2)()
-        querset = [i for i in xrange(20)]
+        querset = [i for i in range(20)]
 
         results = paginator.paginate_queryset(querset, MockRequest())
         self.assertEqual(results, [0, 1, 2, 3, 4, 5])
@@ -57,7 +57,7 @@ class PaginatorTests(TestCase):
     def test_next_page(self):
         """pagination works for next page of queryset"""
         paginator = ApiPaginator(6, 2)()
-        querset = [i for i in xrange(20)]
+        querset = [i for i in range(20)]
 
         results = paginator.paginate_queryset(querset, MockRequest(2))
         self.assertEqual(results, [6, 7, 8, 9, 10, 11])
@@ -78,7 +78,7 @@ class PaginatorTests(TestCase):
     def test_last_page(self):
         """pagination works for last page of queryset"""
         paginator = ApiPaginator(6, 2)()
-        querset = [i for i in xrange(20)]
+        querset = [i for i in range(20)]
 
         results = paginator.paginate_queryset(querset, MockRequest(3))
         self.assertEqual(results, [12, 13, 14, 15, 16, 17, 18, 19])

+ 2 - 4
misago/core/tests/test_decorators.py

@@ -9,11 +9,9 @@ class RequirePostTests(TestCase):
     def test_require_POST_success(self):
         """require_POST decorator allowed POST request"""
         response = self.client.post(reverse('test-require-post'))
-        self.assertEqual(response.status_code, 200)
-        self.assertEqual(response.content, 'Request method: POST')
+        self.assertContains(response, 'Request method: POST')
 
     def test_require_POST_fail_GET(self):
         """require_POST decorator failed on GET request"""
         response = self.client.get(reverse('test-require-post'))
-        self.assertEqual(response.status_code, 405)
-        self.assertIn("Wrong way", response.content)
+        self.assertContains(response, "Wrong way", status_code=405)

+ 11 - 18
misago/core/tests/test_errorpages.py

@@ -13,8 +13,7 @@ class CSRFErrorViewTests(TestCase):
         csrf_client = Client(enforce_csrf_checks=True)
         response = csrf_client.post(reverse('misago:index'),
                                     data={'eric': 'fish'})
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("Request blocked", response.content)
+        self.assertContains(response, "Request blocked", status_code=403)
 
 
 class ErrorPageViewsTests(TestCase):
@@ -23,26 +22,22 @@ class ErrorPageViewsTests(TestCase):
     def test_banned_returns_403(self):
         """banned error page has no show-stoppers"""
         response = self.client.get(reverse('raise-misago-banned'))
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("<p>Banned for test!</p>", response.content)
+        self.assertContains(response, "<p>Banned for test!</p>", status_code=403)
 
     def test_permission_denied_returns_403(self):
         """permission_denied error page has no show-stoppers"""
         response = self.client.get(reverse('raise-misago-403'))
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("Page not available", response.content)
+        self.assertContains(response, "Page not available", status_code=403)
 
     def test_page_not_found_returns_404(self):
         """page_not_found error page has no show-stoppers"""
         response = self.client.get(reverse('raise-misago-404'))
-        self.assertEqual(response.status_code, 404)
-        self.assertIn("Page not found", response.content)
+        self.assertContains(response, "Page not found", status_code=404)
 
     def test_not_allowed_returns_405(self):
         """not allowed error page has no showstoppers"""
         response = self.client.get(reverse('raise-misago-405'))
-        self.assertEqual(response.status_code, 405)
-        self.assertIn("Wrong way", response.content)
+        self.assertContains(response, "Wrong way", status_code=405)
 
 
 class CustomErrorPagesTests(TestCase):
@@ -63,23 +58,21 @@ class CustomErrorPagesTests(TestCase):
         response = self.client.get(reverse('raise-misago-403'))
         self.assertEqual(response.status_code, 403)
         response = self.client.get(reverse('raise-403'))
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("Custom 403", response.content)
+        self.assertContains(response, "Custom 403", status_code=403)
 
         response = mock_custom_403_error_page(self.misago_request)
-        self.assertNotIn("Custom 403", response.content)
+        self.assertNotContains(response, "Custom 403", status_code=403)
         response = mock_custom_403_error_page(self.site_request)
-        self.assertIn("Custom 403", response.content)
+        self.assertContains(response, "Custom 403", status_code=403)
 
     def test_shared_404_decorator(self):
         """shared_404_decorator calls correct error handler"""
         response = self.client.get(reverse('raise-misago-404'))
         self.assertEqual(response.status_code, 404)
         response = self.client.get(reverse('raise-404'))
-        self.assertEqual(response.status_code, 404)
-        self.assertIn("Custom 404", response.content)
+        self.assertContains(response, "Custom 404", status_code=404)
 
         response = mock_custom_404_error_page(self.misago_request)
-        self.assertNotIn("Custom 404", response.content)
+        self.assertNotContains(response, "Custom 404", status_code=404)
         response = mock_custom_404_error_page(self.site_request)
-        self.assertIn("Custom 404", response.content)
+        self.assertContains(response, "Custom 404", status_code=404)

+ 2 - 1
misago/core/tests/test_serializer.py

@@ -1,3 +1,4 @@
+from django.utils.six.moves import range
 from django.test import TestCase
 
 from .. import serializer
@@ -17,7 +18,7 @@ class SerializerTests(TestCase):
 
     def test_serializer_handles_paddings(self):
         """serializer handles missing paddings"""
-        for i in xrange(100):
+        for i in range(100):
             wet = 'Lorem ipsum %s' % ('a' * i)
             dry = serializer.dumps(wet)
             self.assertFalse(dry.endswith('='))

+ 3 - 2
misago/core/tests/test_setup.py

@@ -1,5 +1,6 @@
 import os
 
+from django.utils.encoding import smart_str
 from django.test import TestCase
 
 from .. import setup
@@ -36,5 +37,5 @@ class SetupTests(TestCase):
             os.path.dirname(os.path.dirname(__file__)))
         test_project_path = os.path.join(misago_path, 'project_template')
 
-        self.assertEqual(unicode(setup.get_misago_project_template()),
-                         unicode(test_project_path))
+        self.assertEqual(smart_str(setup.get_misago_project_template()),
+                         smart_str(test_project_path))

+ 3 - 3
misago/core/tests/test_shortcuts.py

@@ -12,7 +12,7 @@ class PaginateTests(TestCase):
         """Valid page number causes no errors"""
         response = self.client.get(
             reverse('test-pagination', kwargs={'page': 2}))
-        self.assertEqual("5,6,7,8,9", response.content)
+        self.assertEqual("5,6,7,8,9", response.content.decode())
 
     def test_invalid_page_handling(self):
         """Invalid page number results in 404 error"""
@@ -24,7 +24,7 @@ class PaginateTests(TestCase):
         """Implicit page number causes no errors"""
         response = self.client.get(
             reverse('test-pagination'))
-        self.assertEqual("0,1,2,3,4", response.content)
+        self.assertEqual("0,1,2,3,4", response.content.decode())
 
     def test_explicit_page_handling(self):
         """Explicit page number results in redirect"""
@@ -43,7 +43,7 @@ class ValidateSlugTests(TestCase):
             'slug': 'eric-the-fish',
             'pk': 1,
         }))
-        self.assertIn("Allright", response.content)
+        self.assertContains(response, "Allright")
 
     def test_invalid_slug_handling(self):
         """Invalid slug returns in redirect to valid page"""

+ 2 - 3
misago/core/tests/test_views.py

@@ -8,12 +8,11 @@ class MomentJSCatalogViewTests(TestCase):
         with self.settings(LANGUAGE_CODE='en_us'):
             response = self.client.get('/moment-i18n.js')
             self.assertEqual(response.status_code, 200)
-            self.assertEqual(response.content, "")
+            self.assertEqual(response.content, b"")
 
         with self.settings(LANGUAGE_CODE='pl_pl'):
             response = self.client.get('/moment-i18n.js')
-            self.assertEqual(response.status_code, 200)
-            self.assertIn(response.content, "// locale : polish (pl)")
+            self.assertContains(response, "// locale : polish (pl)")
 
 
 class PreloadJSDataViewTests(TestCase):

+ 4 - 4
misago/legal/tests.py

@@ -43,8 +43,8 @@ class PrivacyPolicyTests(TestCase):
 
         response = self.client.get(reverse('misago:privacy-policy'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn('Test Policy', response.content)
-        self.assertIn('Lorem ipsum dolor', response.content)
+        self.assertContains(response, 'Test Policy')
+        self.assertContains(response, 'Lorem ipsum dolor')
 
     def test_context_processor_no_policy(self):
         """context processor has no TOS link"""
@@ -110,8 +110,8 @@ class TermsOfServiceTests(TestCase):
 
         response = self.client.get(reverse('misago:terms-of-service'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn('Test ToS', response.content)
-        self.assertIn('Lorem ipsum dolor', response.content)
+        self.assertContains(response, 'Test ToS')
+        self.assertContains(response, 'Lorem ipsum dolor')
 
     def test_context_processor_no_tos(self):
         """context processor has no TOS link"""

+ 5 - 4
misago/threads/tests/test_subscriptions.py

@@ -3,6 +3,7 @@ from datetime import timedelta
 from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.utils import timezone
+from django.utils.six.moves import range
 
 from misago.categories.models import Category
 from misago.users.models import AnonymousUser
@@ -34,7 +35,7 @@ class SubscriptionsTests(TestCase):
     def test_anon_threads_subscription(self):
         """make multiple threads list sub aware for anon"""
         threads = []
-        for i in xrange(10):
+        for i in range(10):
             threads.append(
                 self.post_thread(timezone.now() - timedelta(days=10)))
 
@@ -51,7 +52,7 @@ class SubscriptionsTests(TestCase):
     def test_threads_no_subscription(self):
         """make mulitple threads sub aware for authenticated"""
         threads = []
-        for i in xrange(10):
+        for i in range(10):
             threads.append(
                 self.post_thread(timezone.now() - timedelta(days=10)))
 
@@ -76,7 +77,7 @@ class SubscriptionsTests(TestCase):
     def test_threads_no_subscription(self):
         """make mulitple threads sub aware for authenticated"""
         threads = []
-        for i in xrange(10):
+        for i in range(10):
             threads.append(
                 self.post_thread(timezone.now() - timedelta(days=10)))
 
@@ -99,7 +100,7 @@ class SubscriptionsTests(TestCase):
 
         make_subscription_aware(self.user, threads)
 
-        for i in xrange(10):
+        for i in range(10):
             if i % 3 == 0:
                 self.assertFalse(threads[i].subscription.send_email)
             elif i % 2 == 0:

+ 3 - 2
misago/threads/tests/test_synchronizethreads.py

@@ -1,5 +1,6 @@
 from django.test import TestCase
 from django.utils.six import StringIO
+from django.utils.six.moves import range
 
 from misago.categories.models import Category
 
@@ -22,9 +23,9 @@ class SynchronizeThreadsTests(TestCase):
         """command synchronizes threads"""
         category = Category.objects.all_categories()[:1][0]
 
-        threads = [testutils.post_thread(category) for t in xrange(10)]
+        threads = [testutils.post_thread(category) for t in range(10)]
         for i, thread in enumerate(threads):
-            [testutils.reply_thread(thread) for r in xrange(i)]
+            [testutils.reply_thread(thread) for r in range(i)]
             thread.replies = 0
             thread.save()
 

+ 18 - 16
misago/threads/tests/test_thread_patch_api.py

@@ -1,4 +1,6 @@
 import json
+from django.utils import six
+from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
@@ -55,7 +57,7 @@ class ThreadPinGloballyApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to pin this thread globally.")
 
@@ -80,7 +82,7 @@ class ThreadPinGloballyApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to change this thread's weight.")
 
@@ -137,7 +139,7 @@ class ThreadPinLocallyApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to change this thread's weight.")
 
@@ -162,7 +164,7 @@ class ThreadPinLocallyApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to change this thread's weight.")
 
@@ -223,7 +225,7 @@ class ThreadMoveApiTests(ThreadsApiTestCase):
         thread_json = self.get_thread_json()
         self.assertEqual(thread_json['category']['id'], self.category_b.pk)
 
-        reponse_json = json.loads(response.content)
+        reponse_json = json.loads(smart_str(response.content))
         self.assertEqual(reponse_json['category'], self.category_b.pk)
         self.assertEqual(reponse_json['top_category'], None)
 
@@ -251,7 +253,7 @@ class ThreadMoveApiTests(ThreadsApiTestCase):
         thread_json = self.get_thread_json()
         self.assertEqual(thread_json['category']['id'], self.category_b.pk)
 
-        reponse_json = json.loads(response.content)
+        reponse_json = json.loads(smart_str(response.content))
         self.assertEqual(reponse_json['category'], self.category_b.pk)
         self.assertEqual(reponse_json['top_category'], self.category.pk)
 
@@ -268,7 +270,7 @@ class ThreadMoveApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to move this thread.")
 
@@ -292,7 +294,7 @@ class ThreadMoveApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0], 'NOT FOUND')
 
         self.override_other_acl({})
@@ -315,7 +317,7 @@ class ThreadMoveApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             'You don\'t have permission to browse "Category B" contents.')
 
@@ -332,7 +334,7 @@ class ThreadMoveApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['category'], self.category.pk)
 
     def test_thread_top_flatten_categories(self):
@@ -353,7 +355,7 @@ class ThreadMoveApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['top_category'], self.category.pk)
         self.assertEqual(response_json['category'], self.category_b.pk)
 
@@ -407,7 +409,7 @@ class ThreadCloseApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to close this thread.")
 
@@ -432,7 +434,7 @@ class ThreadCloseApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to open this thread.")
 
@@ -471,7 +473,7 @@ class ThreadApproveApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "Content approval can't be reversed.")
 
@@ -537,7 +539,7 @@ class ThreadHideApiTests(ThreadsApiTestCase):
         content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'][0],
             "You don't have permission to hide this thread.")
 
@@ -626,7 +628,7 @@ class ThreadSubscribeApiTests(ThreadsApiTestCase):
     def test_subscribe_nonexistant_thread(self):
         """api makes it impossible to subscribe nonexistant thread"""
         bad_api_link = self.api_link.replace(
-            unicode(self.thread.pk), unicode(self.thread.pk + 9))
+            six.text_type(self.thread.pk), six.text_type(self.thread.pk + 9))
 
         response = self.client.patch(bad_api_link, json.dumps([
             {'op': 'replace', 'path': 'subscription', 'value': 'email'}

+ 4 - 2
misago/threads/tests/test_threads_api.py

@@ -1,5 +1,7 @@
 import json
 
+from django.utils.encoding import smart_str
+
 from misago.acl.testutils import override_acl
 from misago.categories.models import THREADS_ROOT_NAME, Category
 from misago.users.testutils import AuthenticatedUserTestCase
@@ -47,7 +49,7 @@ class ThreadsApiTestCase(AuthenticatedUserTestCase):
         response = self.client.get(self.thread.get_api_url())
         self.assertEqual(response.status_code, 200)
 
-        return json.loads(response.content)
+        return json.loads(smart_str(response.content))
 
 
 class ThreadRetrieveApiTests(ThreadsApiTestCase):
@@ -141,7 +143,7 @@ class ThreadDeleteApiTests(ThreadsApiTestCase):
             'can_hide_threads': 0
         })
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['detail'],
             "You don't have permission to delete this thread.")
 

+ 21 - 20
misago/threads/tests/test_threads_merge_api.py

@@ -1,6 +1,7 @@
 import json
 
 from django.core.urlresolvers import reverse
+from django.utils.encoding import smart_str
 
 from misago.acl import add_acl
 from misago.acl.testutils import override_acl
@@ -29,7 +30,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         response = self.client.post(self.api_link, content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'detail': "You have to select at least two threads to merge."
         })
@@ -41,7 +42,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'detail': "You have to select at least two threads to merge."
         })
@@ -53,7 +54,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'detail': "One or more thread ids received were invalid."
         })
@@ -63,7 +64,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'detail': "One or more thread ids received were invalid."
         })
@@ -75,7 +76,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'detail': "You have to select at least two threads to merge."
         })
@@ -89,7 +90,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'detail': "One or more threads to merge could not be found."
         })
@@ -103,7 +104,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'detail': "One or more threads to merge could not be found."
         })
@@ -117,7 +118,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 403)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, [
             {
                 'id': thread.pk,
@@ -174,7 +175,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'title': ['This field is required.'],
             'category': ['This field is required.'],
@@ -198,7 +199,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'title': ["Thread title should be at least 5 characters long."]
         })
@@ -221,7 +222,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'category': ["Requested category could not be found."]
         })
@@ -245,7 +246,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'weight': ["Ensure this value is less than or equal to 2."]
         })
@@ -269,7 +270,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'weight': [
                 "You don't have permission to pin threads globally in this category."
@@ -295,7 +296,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'weight': [
                 "You don't have permission to pin threads in this category."
@@ -322,7 +323,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'title': ["Thread title should be at least 5 characters long."]
         })
@@ -347,7 +348,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'title': ["Thread title should be at least 5 characters long."]
         })
@@ -371,7 +372,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'is_closed': [
                 "You don't have permission to close threads in this category."
@@ -399,7 +400,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         }), content_type="application/json")
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json, {
             'title': ["Thread title should be at least 5 characters long."]
         })
@@ -425,7 +426,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         self.assertEqual(response.status_code, 200)
 
         # is response json with new thread?
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
 
         new_thread = Thread.objects.get(pk=response_json['id'])
         new_thread.is_read = False
@@ -466,7 +467,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         self.assertEqual(response.status_code, 200)
 
         # is response json with new thread?
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
 
         new_thread = Thread.objects.get(pk=response_json['id'])
         new_thread.is_read = False

+ 179 - 155
misago/threads/tests/test_threadslists.py

@@ -3,6 +3,8 @@ from json import loads as json_loads
 
 from django.conf import settings
 from django.utils import timezone
+from django.utils.encoding import smart_str
+from django.utils.six.moves import range
 
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
@@ -173,21 +175,21 @@ class AllThreadsListTests(ThreadsListTestCase):
 
             response = self.client.get('/' + url)
             self.assertEqual(response.status_code, 200)
-            self.assertIn("empty-message", response.content)
+            self.assertContains(response, "empty-message")
 
             self.access_all_categories()
 
             response = self.client.get(self.category_b.get_absolute_url() + url)
             self.assertEqual(response.status_code, 200)
-            self.assertIn(self.category_b.name, response.content)
-            self.assertIn("empty-message", response.content)
+            self.assertContains(response, self.category_b.name)
+            self.assertContains(response, "empty-message")
 
             self.access_all_categories()
 
             response = self.client.get('%s?list=%s' % (self.api_link, url.strip('/') or 'all'))
             self.assertEqual(response.status_code, 200)
 
-            response_json = json_loads(response.content)
+            response_json = json_loads(smart_str(response.content))
             self.assertEqual(len(response_json['results']), 0)
 
     def test_list_authenticated_only_views(self):
@@ -202,7 +204,7 @@ class AllThreadsListTests(ThreadsListTestCase):
 
             response = self.client.get(self.category_b.get_absolute_url() + url)
             self.assertEqual(response.status_code, 200)
-            self.assertIn(self.category_b.name, response.content)
+            self.assertContains(response, self.category_b.name)
 
             self.access_all_categories()
 
@@ -249,22 +251,28 @@ class AllThreadsListTests(ThreadsListTestCase):
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
 
-        self.assertIn('subcategory-%s' % self.category_a.css_class, response.content)
+        self.assertContains(response,
+            'subcategory-%s' % self.category_a.css_class)
 
         # readable categories, but non-accessible directly
-        self.assertNotIn('subcategory-%s' % self.category_b.css_class, response.content)
-        self.assertNotIn('subcategory-%s' % self.category_c.css_class, response.content)
-        self.assertNotIn('subcategory-%s' % self.category_d.css_class, response.content)
-        self.assertNotIn('subcategory-%s' % self.category_f.css_class, response.content)
+        self.assertNotContains(response,
+            'subcategory-%s' % self.category_b.css_class)
+        self.assertNotContains(response,
+            'subcategory-%s' % self.category_c.css_class)
+        self.assertNotContains(response,
+            'subcategory-%s' % self.category_d.css_class)
+        self.assertNotContains(response,
+            'subcategory-%s' % self.category_f.css_class)
 
         # hidden category
-        self.assertNotIn('subcategory-%s' % test_category.css_class, response.content)
+        self.assertNotContains(response,
+            'subcategory-%s' % test_category.css_class)
 
         self.access_all_categories()
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertIn(self.category_a.pk, response_json['subcategories'])
         self.assertNotIn(self.category_b.pk, response_json['subcategories'])
 
@@ -274,18 +282,24 @@ class AllThreadsListTests(ThreadsListTestCase):
         response = self.client.get(self.category_a.get_absolute_url())
         self.assertEqual(response.status_code, 200)
 
-        self.assertIn('subcategory-%s' % self.category_b.css_class, response.content)
+        self.assertContains(response,
+            'subcategory-%s' % self.category_b.css_class)
 
         # readable categories, but non-accessible directly
-        self.assertNotIn('subcategory-%s' % self.category_c.css_class, response.content)
-        self.assertNotIn('subcategory-%s' % self.category_d.css_class, response.content)
-        self.assertNotIn('subcategory-%s' % self.category_f.css_class, response.content)
+        self.assertNotContains(response,
+            'subcategory-%s' % self.category_c.css_class)
+        self.assertNotContains(response,
+            'subcategory-%s' % self.category_d.css_class)
+        self.assertNotContains(response,
+            'subcategory-%s' % self.category_f.css_class)
 
         self.access_all_categories()
         response = self.client.get('%s?category=%s' % (self.api_link, self.category_a.pk))
+        response = self.client.get(
+            '%s?category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(
             response_json['subcategories'][0], self.category_b.pk)
 
@@ -311,10 +325,11 @@ class AllThreadsListTests(ThreadsListTestCase):
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
 
+        content = smart_str(response.content)
         positions = {
-            'g': response.content.find(globally.get_absolute_url()),
-            'l': response.content.find(locally.get_absolute_url()),
-            's': response.content.find(standard.get_absolute_url()),
+            'g': content.find(globally.get_absolute_url()),
+            'l': content.find(locally.get_absolute_url()),
+            's': content.find(standard.get_absolute_url()),
         }
 
         # global announcement before others
@@ -333,10 +348,11 @@ class AllThreadsListTests(ThreadsListTestCase):
         response = self.client.get('/api/threads/')
         self.assertEqual(response.status_code, 200)
 
+        content = smart_str(response.content)
         positions = {
-            'g': response.content.find(globally.get_absolute_url()),
-            'l': response.content.find(locally.get_absolute_url()),
-            's': response.content.find(standard.get_absolute_url()),
+            'g': content.find(globally.get_absolute_url()),
+            'l': content.find(locally.get_absolute_url()),
+            's': content.find(standard.get_absolute_url()),
         }
 
         # global announcement before others
@@ -354,7 +370,7 @@ class AllThreadsListTests(ThreadsListTestCase):
     def test_noscript_pagination(self):
         """threads list is paginated for users with js disabled"""
         threads = []
-        for i in xrange(settings.MISAGO_THREADS_PER_PAGE * 3):
+        for i in range(settings.MISAGO_THREADS_PER_PAGE * 3):
             threads.append(testutils.post_thread(
                 category=self.first_category
             ))
@@ -364,26 +380,26 @@ class AllThreadsListTests(ThreadsListTestCase):
         self.assertEqual(response.status_code, 200)
 
         for thread in threads[:settings.MISAGO_THREADS_PER_PAGE]:
-            self.assertNotIn(thread.get_absolute_url(), response.content)
+            self.assertNotContains(response, thread.get_absolute_url())
         for thread in threads[settings.MISAGO_THREADS_PER_PAGE:settings.MISAGO_THREADS_PER_PAGE * 2]:
-            self.assertIn(thread.get_absolute_url(), response.content)
+            self.assertContains(response, thread.get_absolute_url())
         for thread in threads[settings.MISAGO_THREADS_PER_PAGE * 2:]:
-            self.assertNotIn(thread.get_absolute_url(), response.content)
+            self.assertNotContains(response, thread.get_absolute_url())
 
-        self.assertNotIn('/?page=1', response.content)
-        self.assertIn('/?page=3', response.content)
+        self.assertNotContains(response, '/?page=1')
+        self.assertContains(response, '/?page=3')
 
         # third page renders
         response = self.client.get('/?page=3')
         self.assertEqual(response.status_code, 200)
 
         for thread in threads[settings.MISAGO_THREADS_PER_PAGE:]:
-            self.assertNotIn(thread.get_absolute_url(), response.content)
+            self.assertNotContains(response, thread.get_absolute_url())
         for thread in threads[:settings.MISAGO_THREADS_PER_PAGE]:
-            self.assertIn(thread.get_absolute_url(), response.content)
+            self.assertContains(response, thread.get_absolute_url())
 
-        self.assertIn('/?page=2', response.content)
-        self.assertNotIn('/?page=4', response.content)
+        self.assertContains(response, '/?page=2')
+        self.assertNotContains(response, '/?page=4')
 
         # excessive page gives 404
         response = self.client.get('/?page=4')
@@ -469,10 +485,11 @@ class CategoryThreadsListTests(ThreadsListTestCase):
         response = self.client.get(self.first_category.get_absolute_url())
         self.assertEqual(response.status_code, 200)
 
+        content = smart_str(response.content)
         positions = {
-            'g': response.content.find(globally.get_absolute_url()),
-            'l': response.content.find(locally.get_absolute_url()),
-            's': response.content.find(standard.get_absolute_url()),
+            'g': content.find(globally.get_absolute_url()),
+            'l': content.find(locally.get_absolute_url()),
+            's': content.find(standard.get_absolute_url()),
         }
 
         # global announcement before others
@@ -491,10 +508,11 @@ class CategoryThreadsListTests(ThreadsListTestCase):
         response = self.client.get('/api/threads/?category=%s' % self.first_category.pk)
         self.assertEqual(response.status_code, 200)
 
+        content = smart_str(response.content)
         positions = {
-            'g': response.content.find(globally.get_absolute_url()),
-            'l': response.content.find(locally.get_absolute_url()),
-            's': response.content.find(standard.get_absolute_url()),
+            'g': content.find(globally.get_absolute_url()),
+            'l': content.find(locally.get_absolute_url()),
+            's': content.find(standard.get_absolute_url()),
         }
 
         # global announcement before others
@@ -520,19 +538,23 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
 
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
-        self.assertIn('subcategory-%s' % self.category_a.css_class, response.content)
-        self.assertIn('subcategory-%s' % self.category_e.css_class, response.content)
-        self.assertIn('thread-category-%s' % self.category_a.css_class, response.content)
-        self.assertIn('thread-category-%s' % self.category_c.css_class, response.content)
+        self.assertContains(response,
+            'subcategory-%s' % self.category_a.css_class)
+        self.assertContains(response,
+            'subcategory-%s' % self.category_e.css_class)            
+        self.assertContains(response,
+            'thread-category-%s' % self.category_a.css_class)
+        self.assertContains(response,
+            'thread-category-%s' % self.category_c.css_class)
 
         # api displays same data
         self.access_all_categories()
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
         self.assertEqual(len(response_json['subcategories']), 3)
         self.assertIn(self.category_a.pk, response_json['subcategories'])
@@ -543,17 +565,19 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         self.assertEqual(response.status_code, 200)
 
         # thread displays
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
-        self.assertNotIn('thread-category-%s' % self.category_b.css_class, response.content)
-        self.assertIn('thread-category-%s' % self.category_c.css_class, response.content)
+        self.assertNotContains(response,
+            'thread-category-%s' % self.category_b.css_class)
+        self.assertContains(response,
+            'thread-category-%s' % self.category_c.css_class)
 
         # api displays same data
         self.access_all_categories()
         response = self.client.get('%s?category=%s' % (self.api_link, self.category_b.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
         self.assertEqual(len(response_json['subcategories']), 2)
         self.assertEqual(response_json['subcategories'][0], self.category_c.pk)
@@ -572,7 +596,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn("empty-message", response.content)
+        self.assertContains(response, "empty-message")
 
     def test_api_hides_hidden_thread(self):
         """api returns empty due to no permission to see thread"""
@@ -589,7 +613,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_user_see_own_unapproved_thread(self):
@@ -602,14 +626,14 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
     def test_list_user_cant_see_unapproved_thread(self):
@@ -621,14 +645,14 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_user_cant_see_hidden_thread(self):
@@ -640,14 +664,14 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_user_cant_see_own_hidden_thread(self):
@@ -660,14 +684,14 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_user_can_see_own_hidden_thread(self):
@@ -684,7 +708,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories({
@@ -694,7 +718,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
     def test_list_user_can_see_hidden_thread(self):
@@ -712,7 +736,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories({
@@ -722,7 +746,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
     def test_list_user_can_see_unapproved_thread(self):
@@ -740,7 +764,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
 
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories({
@@ -750,7 +774,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
 
@@ -761,26 +785,26 @@ class MyThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/my/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn("empty-message", response.content)
+        self.assertContains(response, "empty-message")
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'my/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn("empty-message", response.content)
+        self.assertContains(response, "empty-message")
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=my' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=my&category=%s' % (self.api_link, self.category_a.pk))
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_renders_test_thread(self):
@@ -798,22 +822,22 @@ class MyThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/my/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
-        self.assertNotIn(other_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
+        self.assertNotContains(response, other_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'my/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
-        self.assertNotIn(other_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
+        self.assertNotContains(response, other_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=my' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -821,7 +845,7 @@ class MyThreadsListTests(ThreadsListTestCase):
         response = self.client.get('%s?list=my&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -833,26 +857,26 @@ class NewThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/new/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn("empty-message", response.content)
+        self.assertContains(response, "empty-message")
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'new/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn("empty-message", response.content)
+        self.assertContains(response, "empty-message")
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=new' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_renders_new_thread(self):
@@ -865,20 +889,20 @@ class NewThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/new/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'new/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=new' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -886,7 +910,7 @@ class NewThreadsListTests(ThreadsListTestCase):
         response = self.client.get('%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -908,20 +932,20 @@ class NewThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/new/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'new/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=new' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -929,7 +953,7 @@ class NewThreadsListTests(ThreadsListTestCase):
         response = self.client.get('%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -949,27 +973,27 @@ class NewThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=new' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_hides_user_cutoff_thread(self):
@@ -986,27 +1010,27 @@ class NewThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=new' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_hides_user_read_thread(self):
@@ -1025,27 +1049,27 @@ class NewThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=new' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_hides_category_read_thread(self):
@@ -1066,27 +1090,27 @@ class NewThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'new/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=new' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=new&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
 
@@ -1097,27 +1121,27 @@ class UnreadThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn("empty-message", response.content)
+        self.assertContains(response, "empty-message")
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn("empty-message", response.content)
+        self.assertContains(response, "empty-message")
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=unread' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_renders_unread_thread(self):
@@ -1138,20 +1162,20 @@ class UnreadThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=unread' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -1159,7 +1183,7 @@ class UnreadThreadsListTests(ThreadsListTestCase):
         response = self.client.get('%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
@@ -1176,27 +1200,27 @@ class UnreadThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=unread' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_hides_read_thread(self):
@@ -1215,27 +1239,27 @@ class UnreadThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=unread' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_hides_global_cutoff_thread(self):
@@ -1259,27 +1283,27 @@ class UnreadThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=unread' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_hides_user_cutoff_thread(self):
@@ -1301,27 +1325,27 @@ class UnreadThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=unread' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
     def test_list_hides_category_cutoff_thread(self):
@@ -1349,27 +1373,27 @@ class UnreadThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'unread/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=unread' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
         self.access_all_categories()
         response = self.client.get('%s?list=unread&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
 
 
@@ -1389,30 +1413,30 @@ class SubscribedThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/subscribed/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'subscribed/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=subscribed' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
         response = self.client.get('%s?list=subscribed&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 1)
-        self.assertIn(test_thread.get_absolute_url(), response.content)
+        self.assertContains(response, test_thread.get_absolute_url())
 
     def test_list_hides_unsubscribed_thread(self):
         """list shows subscribed thread"""
@@ -1424,30 +1448,30 @@ class SubscribedThreadsListTests(ThreadsListTestCase):
 
         response = self.client.get('/subscribed/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
 
         response = self.client.get(self.category_a.get_absolute_url() + 'subscribed/')
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         # test api
         self.access_all_categories()
         response = self.client.get('%s?list=subscribed' % self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
         self.access_all_categories()
         response = self.client.get('%s?list=subscribed&category=%s' % (self.api_link, self.category_a.pk))
         self.assertEqual(response.status_code, 200)
 
-        response_json = json_loads(response.content)
+        response_json = json_loads(smart_str(response.content))
         self.assertEqual(len(response_json['results']), 0)
-        self.assertNotIn(test_thread.get_absolute_url(), response.content)
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
 
 class UnapprovedListTests(ThreadsListTestCase):
@@ -1503,8 +1527,8 @@ class UnapprovedListTests(ThreadsListTestCase):
         })
         response = self.client.get('/unapproved/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(visible_thread.get_absolute_url(), response.content)
-        self.assertNotIn(hidden_thread.get_absolute_url(), response.content)
+        self.assertContains(response, visible_thread.get_absolute_url())
+        self.assertNotContains(response, hidden_thread.get_absolute_url())
 
         self.access_all_categories({
             'can_approve_content': True
@@ -1513,8 +1537,8 @@ class UnapprovedListTests(ThreadsListTestCase):
         })
         response = self.client.get(self.category_a.get_absolute_url() + 'unapproved/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(visible_thread.get_absolute_url(), response.content)
-        self.assertNotIn(hidden_thread.get_absolute_url(), response.content)
+        self.assertContains(response, visible_thread.get_absolute_url())
+        self.assertNotContains(response, hidden_thread.get_absolute_url())
 
         # test api
         self.access_all_categories({
@@ -1524,8 +1548,8 @@ class UnapprovedListTests(ThreadsListTestCase):
         })
         response = self.client.get('%s?list=unapproved' % self.api_link)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(visible_thread.get_absolute_url(), response.content)
-        self.assertNotIn(hidden_thread.get_absolute_url(), response.content)
+        self.assertContains(response, visible_thread.get_absolute_url())
+        self.assertNotContains(response, hidden_thread.get_absolute_url())
 
     def test_list_shows_owned_threads_for_unapproving_user(self):
         """
@@ -1547,16 +1571,16 @@ class UnapprovedListTests(ThreadsListTestCase):
         })
         response = self.client.get('/unapproved/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(visible_thread.get_absolute_url(), response.content)
-        self.assertNotIn(hidden_thread.get_absolute_url(), response.content)
+        self.assertContains(response, visible_thread.get_absolute_url())
+        self.assertNotContains(response, hidden_thread.get_absolute_url())
 
         self.access_all_categories(base_acl={
             'can_see_unapproved_content_lists': True
         })
         response = self.client.get(self.category_a.get_absolute_url() + 'unapproved/')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(visible_thread.get_absolute_url(), response.content)
-        self.assertNotIn(hidden_thread.get_absolute_url(), response.content)
+        self.assertContains(response, visible_thread.get_absolute_url())
+        self.assertNotContains(response, hidden_thread.get_absolute_url())
 
         # test api
         self.access_all_categories(base_acl={
@@ -1564,5 +1588,5 @@ class UnapprovedListTests(ThreadsListTestCase):
         })
         response = self.client.get('%s?list=unapproved' % self.api_link)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(visible_thread.get_absolute_url(), response.content)
-        self.assertNotIn(hidden_thread.get_absolute_url(), response.content)
+        self.assertContains(response, visible_thread.get_absolute_url())
+        self.assertNotContains(response, hidden_thread.get_absolute_url())

+ 2 - 3
misago/users/tests/test_activation_views.py

@@ -29,8 +29,7 @@ class ActivationViewsTests(TestCase):
             'pk': test_user.pk,
             'token': activation_token,
         }))
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("<p>Nope!</p>", response.content)
+        self.assertContains(response, "<p>Nope!</p>", status_code=403)
 
         test_user = User.objects.get(pk=test_user.pk)
         self.assertEqual(test_user.requires_activation, 1)
@@ -81,7 +80,7 @@ class ActivationViewsTests(TestCase):
             'token': activation_token,
         }))
         self.assertEqual(response.status_code, 200)
-        self.assertIn("your account has been activated!", response.content)
+        self.assertContains(response, "your account has been activated!")
 
         test_user = User.objects.get(pk=test_user.pk)
         self.assertEqual(test_user.requires_activation, 0)

+ 24 - 39
misago/users/tests/test_auth_api.py

@@ -2,6 +2,7 @@ import json
 
 from django.contrib.auth import get_user_model
 from django.core import mail
+from django.utils.encoding import smart_str
 from django.test import TestCase
 
 from ..models import BAN_USERNAME, Ban
@@ -15,13 +16,12 @@ class GatewayTests(TestCase):
             '/api/auth/',
             data={'username': 'nope', 'password': 'nope'})
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn("Login or password is incorrect.", response.content)
+        self.assertContains(response, "Login or password is incorrect.", status_code=400)
 
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])
 
     def test_login(self):
@@ -39,15 +39,14 @@ class GatewayTests(TestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertEqual(user_json['id'], user.id)
         self.assertEqual(user_json['username'], user.username)
 
     def test_submit_empty(self):
         """login api errors for no body"""
         response = self.client.post('/api/auth/')
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('empty_data', response.content)
+        self.assertContains(response, 'empty_data', status_code=400)
 
     def test_login_banned(self):
         """login api fails to sign banned user in"""
@@ -66,7 +65,7 @@ class GatewayTests(TestCase):
         })
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['code'], 'banned')
         self.assertEqual(response_json['detail']['message']['plain'],
                          ban.user_message)
@@ -76,7 +75,7 @@ class GatewayTests(TestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])
 
     def test_login_inactive_admin(self):
@@ -91,13 +90,13 @@ class GatewayTests(TestCase):
         })
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['code'], 'inactive_user')
 
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])
 
     def test_login_inactive_user(self):
@@ -112,13 +111,13 @@ class GatewayTests(TestCase):
         })
         self.assertEqual(response.status_code, 400)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['code'], 'inactive_admin')
 
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])
 
 
@@ -154,16 +153,14 @@ class SendActivationAPITests(TestCase):
     def test_submit_empty(self):
         """request activation link api errors for no body"""
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('empty_email', response.content)
+        self.assertContains(response, 'empty_email', status_code=400)
 
         self.assertTrue(not mail.outbox)
 
     def test_submit_invalid(self):
         """request activation link api errors for invalid email"""
         response = self.client.post(self.link, data={'email': 'fake@mail.com'})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('not_found', response.content)
+        self.assertContains(response, 'not_found', status_code=400)
 
         self.assertTrue(not mail.outbox)
 
@@ -173,9 +170,7 @@ class SendActivationAPITests(TestCase):
         self.user.save()
 
         response = self.client.post(self.link, data={'email': self.user.email})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Bob, your account is already active.',
-                      response.content)
+        self.assertContains(response, 'Bob, your account is already active.', status_code=400)
 
     def test_submit_inactive_user(self):
         """request activation link api errors for admin-activated users"""
@@ -183,8 +178,7 @@ class SendActivationAPITests(TestCase):
         self.user.save()
 
         response = self.client.post(self.link, data={'email': self.user.email})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('inactive_admin', response.content)
+        self.assertContains(response, 'inactive_admin', status_code=400)
 
         self.assertTrue(not mail.outbox)
 
@@ -228,16 +222,14 @@ class SendPasswordFormAPITests(TestCase):
     def test_submit_empty(self):
         """request change password form link api errors for no body"""
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('empty_email', response.content)
+        self.assertContains(response, 'empty_email', status_code=400)
 
         self.assertTrue(not mail.outbox)
 
     def test_submit_invalid(self):
         """request change password form link api errors for invalid email"""
         response = self.client.post(self.link, data={'email': 'fake@mail.com'})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('not_found', response.content)
+        self.assertContains(response, 'not_found', status_code=400)
 
         self.assertTrue(not mail.outbox)
 
@@ -247,15 +239,13 @@ class SendPasswordFormAPITests(TestCase):
         self.user.save()
 
         response = self.client.post(self.link, data={'email': self.user.email})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('inactive_user', response.content)
+        self.assertContains(response, 'inactive_user', status_code=400)
 
         self.user.requires_activation = 2
         self.user.save()
 
         response = self.client.post(self.link, data={'email': self.user.email})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('inactive_admin', response.content)
+        self.assertContains(response, 'inactive_admin', status_code=400)
 
         self.assertTrue(not mail.outbox)
 
@@ -285,8 +275,7 @@ class ChangePasswordAPITests(TestCase):
                 'asda7ad89sa7d9s789as'
             ))
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Form link is invalid.', response.content)
+        self.assertContains(response, 'Form link is invalid.', status_code=400)
 
     def test_banned_user_link(self):
         """request errors because user is banned"""
@@ -300,8 +289,7 @@ class ChangePasswordAPITests(TestCase):
                 self.user.pk,
                 make_password_change_token(self.user)
             ))
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Your link has expired.', response.content)
+        self.assertContains(response, 'Your link has expired.', status_code=400)
 
     def test_inactive_user(self):
         """request change password form link api errors for inactive users"""
@@ -312,8 +300,7 @@ class ChangePasswordAPITests(TestCase):
                 self.user.pk,
                 make_password_change_token(self.user)
             ))
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Your link has expired.', response.content)
+        self.assertContains(response, 'Your link has expired.', status_code=400)
 
         self.user.requires_activation = 2
         self.user.save()
@@ -322,8 +309,7 @@ class ChangePasswordAPITests(TestCase):
                 self.user.pk,
                 make_password_change_token(self.user)
             ))
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Your link has expired.', response.content)
+        self.assertContains(response, 'Your link has expired.', status_code=400)
 
     def test_submit_empty(self):
         """submit change password form api errors for empty body"""
@@ -331,5 +317,4 @@ class ChangePasswordAPITests(TestCase):
                 self.user.pk,
                 make_password_change_token(self.user)
             ))
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Valid password must', response.content)
+        self.assertContains(response, 'Valid password must', status_code=400)

+ 4 - 4
misago/users/tests/test_auth_views.py

@@ -2,6 +2,7 @@ import json
 
 from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
+from django.utils.encoding import smart_str
 from django.test import TestCase
 
 
@@ -43,13 +44,12 @@ class AuthViewsTests(TestCase):
         response = self.client.post(
             '/api/auth/', data={'username': 'nope', 'password': 'nope'})
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn("Login or password is incorrect.", response.content)
+        self.assertContains(response, "Login or password is incorrect.", status_code=400)
 
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])
 
         response = self.client.post(reverse('misago:logout'))
@@ -58,5 +58,5 @@ class AuthViewsTests(TestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])

+ 6 - 6
misago/users/tests/test_banadmin_views.py

@@ -1,6 +1,7 @@
 from datetime import date, datetime, timedelta
 
 from django.core.urlresolvers import reverse
+from django.utils.six.moves import range
 
 from misago.admin.testutils import AdminTestCase
 
@@ -14,8 +15,7 @@ class BanAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:accounts:index'))
 
         response = self.client.get(response['location'])
-        self.assertIn(reverse('misago:admin:users:bans:index'),
-                      response.content)
+        self.assertContains(response, reverse('misago:admin:users:bans:index'))
 
     def test_list_view(self):
         """bans list view returns 200"""
@@ -29,7 +29,7 @@ class BanAdminViewsTests(AdminTestCase):
         """adminview deletes multiple bans"""
         test_date = datetime.now() + timedelta(days=180)
 
-        for i in xrange(10):
+        for i in range(10):
             response = self.client.post(
                 reverse('misago:admin:users:bans:new'),
                 data={
@@ -75,7 +75,7 @@ class BanAdminViewsTests(AdminTestCase):
         response = self.client.get(reverse('misago:admin:users:bans:index'))
         response = self.client.get(response['location'])
         self.assertEqual(response.status_code, 200)
-        self.assertIn('test@test.com', response.content)
+        self.assertContains(response, 'test@test.com')
 
     def test_edit_view(self):
         """edit ban view has no showstoppers"""
@@ -101,7 +101,7 @@ class BanAdminViewsTests(AdminTestCase):
         response = self.client.get(reverse('misago:admin:users:bans:index'))
         response = self.client.get(response['location'])
         self.assertEqual(response.status_code, 200)
-        self.assertIn('test@test.com', response.content)
+        self.assertContains(response, 'test@test.com')
 
     def test_delete_view(self):
         """delete ban view has no showstoppers"""
@@ -125,4 +125,4 @@ class BanAdminViewsTests(AdminTestCase):
         response = self.client.get(response['location'])
 
         self.assertEqual(response.status_code, 200)
-        self.assertTrue(test_ban.banned_value not in response.content)
+        self.assertNotContains(response, test_ban.banned_value)

+ 2 - 1
misago/users/tests/test_bansmaintenance.py

@@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.utils import timezone
 from django.utils.six import StringIO
+from django.utils.six.moves import range
 
 from .. import bans
 from ..management.commands import bansmaintenance
@@ -14,7 +15,7 @@ class BansMaintenanceTests(TestCase):
     def test_expired_bans_handling(self):
         """expired bans are flagged as such"""
         # create 5 bans then update their valid date to past one
-        for i in xrange(5):
+        for i in range(5):
             Ban.objects.create(banned_value="abcd")
         expired_date = (timezone.now() - timedelta(days=10))
         Ban.objects.all().update(expires_on=expired_date, is_checked=True)

+ 2 - 1
misago/users/tests/test_captcha_api.py

@@ -1,6 +1,7 @@
 import json
 
 from django.core.urlresolvers import reverse
+from django.utils.encoding import smart_str
 from django.test import TestCase
 
 from misago.conf import settings
@@ -28,6 +29,6 @@ class AuthenticateAPITests(TestCase):
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['question'], 'Do you like pies?')
         self.assertEqual(response_json['help_text'], 'Type in "yes".')

+ 1 - 2
misago/users/tests/test_decorators.py

@@ -51,5 +51,4 @@ class DenyBannedIPTests(UserTestCase):
             user_message='Ya got banned!')
 
         response = self.client.post(reverse('misago:request-activation'))
-        self.assertEqual(response.status_code, 403)
-        self.assertIn('<p>Ya got banned!</p>', response.content)
+        self.assertContains(response, '<p>Ya got banned!</p>', status_code=403)

+ 4 - 4
misago/users/tests/test_djangoadmin_auth.py

@@ -24,20 +24,20 @@ class DjangoAdminAuthTests(AdminTestCase):
 
         response = self.client.get(reverse('admin:index'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
     def test_logout(self):
         """its possible to sign out from django admin"""
         response = self.client.get(reverse('admin:index'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
         # assert there's no showstopper on signout page
         response = self.client.get(reverse('admin:logout'))
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(self.user.username, response.content)
+        self.assertNotContains(response, self.user.username)
 
         # user was signed out
         response = self.client.get(reverse('admin:index'))
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(self.user.username, response.content)
+        self.assertNotContains(response, self.user.username)

+ 4 - 8
misago/users/tests/test_forgottenpassword_views.py

@@ -37,8 +37,7 @@ class ForgottenPasswordViewsTests(UserTestCase):
                 'pk': test_user.pk,
                 'token': password_token,
             }))
-        self.assertEqual(response.status_code, 403)
-        self.assertIn('<p>Nope!</p>', response.content)
+        self.assertContains(response, '<p>Nope!</p>', status_code=403)
 
     def test_change_password_on_other_user(self):
         """change other user password errors"""
@@ -54,8 +53,7 @@ class ForgottenPasswordViewsTests(UserTestCase):
                 'pk': test_user.pk,
                 'token': password_token,
             }))
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('your link has expired', response.content)
+        self.assertContains(response, 'your link has expired', status_code=400)
 
     def test_change_password_invalid_token(self):
         """invalid form token errors"""
@@ -69,8 +67,7 @@ class ForgottenPasswordViewsTests(UserTestCase):
                 'pk': test_user.pk,
                 'token': 'abcdfghqsads',
             }))
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('your link is invalid', response.content)
+        self.assertContains(response, 'your link is invalid', status_code=400)
 
     def test_change_password_form(self):
         """change user password form displays for valid token"""
@@ -84,5 +81,4 @@ class ForgottenPasswordViewsTests(UserTestCase):
                 'pk': test_user.pk,
                 'token': password_token,
             }))
-        self.assertEqual(response.status_code, 200)
-        self.assertIn(password_token, response.content)
+        self.assertContains(response, password_token)

+ 2 - 1
misago/users/tests/test_lists_views.py

@@ -1,5 +1,6 @@
 from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
+from django.utils.six.moves import range
 
 from misago.acl.testutils import override_acl
 
@@ -43,7 +44,7 @@ class ActivePostersTests(UsersListTestCase):
 
         # Create 200 test users and see if errors appeared
         User = get_user_model()
-        for i in xrange(200):
+        for i in range(200):
             User.objects.create_user(
                 'Bob%s' % i, 'm%s@te.com' % i, 'Pass.123', posts=12345)
 

+ 4 - 8
misago/users/tests/test_options_views.py

@@ -41,15 +41,13 @@ class ConfirmChangeEmailTests(AuthenticatedUserTestCase):
                 'token': 'invalid'
             }))
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn("Change confirmation link is invalid.", response.content)
+        self.assertContains(response, "Change confirmation link is invalid.", status_code=400)
 
     def test_change_email(self):
         """valid token changes email"""
         response = self.client.get(self.link)
 
-        self.assertEqual(response.status_code, 200)
-        self.assertIn("your e-mail has been changed", response.content)
+        self.assertContains(response, "your e-mail has been changed")
 
         self.reload_user()
         self.assertEqual(self.user.email, 'n3w@email.com')
@@ -78,15 +76,13 @@ class ConfirmChangePasswordTests(AuthenticatedUserTestCase):
                 'token': 'invalid'
             }))
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn("Change confirmation link is invalid.", response.content)
+        self.assertContains(response, "Change confirmation link is invalid.", status_code=400)
 
     def test_change_password(self):
         """valid token changes password"""
         response = self.client.get(self.link)
 
-        self.assertEqual(response.status_code, 200)
-        self.assertIn("your password has been changed", response.content)
+        self.assertContains(response, "your password has been changed")
 
         self.reload_user()
         self.assertFalse(self.user.check_password(self.USER_PASSWORD))

+ 16 - 15
misago/users/tests/test_profile_views.py

@@ -1,5 +1,6 @@
 from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
+from django.utils.six.moves import range
 
 from misago.acl.testutils import override_acl
 
@@ -29,7 +30,7 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
                                            kwargs=self.link_kwargs))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn('no messages posted', response.content)
+        self.assertContains(response, 'no messages posted')
 
     def test_user_threads_list(self):
         """user profile threads list has no showstoppers"""
@@ -37,7 +38,7 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
                                            kwargs=self.link_kwargs))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn('no started threads', response.content)
+        self.assertContains(response, 'no started threads')
 
     def test_user_followers(self):
         """user profile followers list has no showstoppers"""
@@ -47,10 +48,10 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
                                            kwargs=self.link_kwargs))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn('You have no followers.', response.content)
+        self.assertContains(response, 'You have no followers.')
 
         followers = []
-        for i in xrange(10):
+        for i in range(10):
             user_data = ("Follower%s" % i, "foll%s@test.com" % i, "Pass.123")
             followers.append(User.objects.create_user(*user_data))
             self.user.followed_by.add(followers[-1])
@@ -58,8 +59,8 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
         response = self.client.get(reverse('misago:user-followers',
                                            kwargs=self.link_kwargs))
         self.assertEqual(response.status_code, 200)
-        for i in xrange(10):
-            self.assertIn("Follower%s" % i, response.content)
+        for i in range(10):
+            self.assertContains(response, "Follower%s" % i)
 
     def test_user_follows(self):
         """user profile follows list has no showstoppers"""
@@ -69,10 +70,10 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
                                            kwargs=self.link_kwargs))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn('You are not following any users.', response.content)
+        self.assertContains(response, 'You are not following any users.')
 
         followers = []
-        for i in xrange(10):
+        for i in range(10):
             user_data = ("Follower%s" % i, "foll%s@test.com" % i, "Pass.123")
             followers.append(User.objects.create_user(*user_data))
             followers[-1].followed_by.add(self.user)
@@ -80,15 +81,15 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
         response = self.client.get(reverse('misago:user-follows',
                                            kwargs=self.link_kwargs))
         self.assertEqual(response.status_code, 200)
-        for i in xrange(10):
-            self.assertIn("Follower%s" % i, response.content)
+        for i in range(10):
+            self.assertContains(response, "Follower%s" % i)
 
     def test_username_history_list(self):
         """user name changes history list has no showstoppers"""
         response = self.client.get(reverse('misago:username-history',
                                            kwargs=self.link_kwargs))
         self.assertEqual(response.status_code, 200)
-        self.assertIn('Your username was never changed.', response.content)
+        self.assertContains(response, 'Your username was never changed.')
 
         self.user.set_username('RenamedAdmin')
         self.user.save()
@@ -98,8 +99,8 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
         response = self.client.get(reverse('misago:username-history',
                                            kwargs=self.link_kwargs))
         self.assertEqual(response.status_code, 200)
-        self.assertIn("TestUser", response.content)
-        self.assertIn("RenamedAdmin", response.content)
+        self.assertContains(response, "TestUser")
+        self.assertContains(response, "RenamedAdmin")
 
     def test_user_ban_details(self):
         """user ban details page has no showstoppers"""
@@ -136,5 +137,5 @@ class UserProfileViewsTests(AuthenticatedUserTestCase):
         response = self.client.get(reverse('misago:user-ban',
                                            kwargs=link_kwargs))
         self.assertEqual(response.status_code, 200)
-        self.assertIn('User m3ss4ge', response.content)
-        self.assertIn('Staff m3ss4ge', response.content)
+        self.assertContains(response, 'User m3ss4ge')
+        self.assertContains(response, 'Staff m3ss4ge')

+ 11 - 12
misago/users/tests/test_rankadmin_views.py

@@ -13,15 +13,14 @@ class RankAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:accounts:index'))
 
         response = self.client.get(response['location'])
-        self.assertIn(reverse('misago:admin:users:ranks:index'),
-                      response.content)
+        self.assertContains(response, reverse('misago:admin:users:ranks:index'))
 
     def test_list_view(self):
         """ranks list view returns 200"""
         response = self.client.get(reverse('misago:admin:users:ranks:index'))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn('Team', response.content)
+        self.assertContains(response, 'Team')
 
     def test_new_view(self):
         """new rank view has no showstoppers"""
@@ -47,8 +46,8 @@ class RankAdminViewsTests(AdminTestCase):
 
         response = self.client.get(reverse('misago:admin:users:ranks:index'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn('Test Rank', response.content)
-        self.assertIn('Test Title', response.content)
+        self.assertContains(response, 'Test Rank')
+        self.assertContains(response, 'Test Title')
 
         test_rank = Rank.objects.get(slug='test-rank')
         self.assertIn(test_role_a, test_rank.roles.all())
@@ -78,8 +77,8 @@ class RankAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:ranks:edit',
                     kwargs={'pk': test_rank.pk}))
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_rank.name, response.content)
-        self.assertIn(test_rank.title, response.content)
+        self.assertContains(response, test_rank.name)
+        self.assertContains(response, test_rank.title)
 
         response = self.client.post(
             reverse('misago:admin:users:ranks:edit',
@@ -93,7 +92,7 @@ class RankAdminViewsTests(AdminTestCase):
         test_rank = Rank.objects.get(slug='top-lel')
         response = self.client.get(reverse('misago:admin:users:ranks:index'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_rank.name, response.content)
+        self.assertContains(response, test_rank.name)
         self.assertTrue('Test Rank' not in test_rank.roles.all())
         self.assertTrue('Test Title' not in test_rank.roles.all())
 
@@ -214,8 +213,8 @@ class RankAdminViewsTests(AdminTestCase):
         response = self.client.get(reverse('misago:admin:users:ranks:index'))
         self.assertEqual(response.status_code, 200)
 
-        self.assertTrue(test_rank.name not in response.content)
-        self.assertTrue(test_rank.title not in response.content)
+        self.assertNotContains(response, test_rank.name)
+        self.assertNotContains(response, test_rank.title)
 
     def test_uniquess(self):
         """rank slug uniqueness is enforced by admin forms"""
@@ -233,7 +232,7 @@ class RankAdminViewsTests(AdminTestCase):
             })
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn("This name collides with other rank.", response.content)
+        self.assertContains(response, "This name collides with other rank.")
 
         self.client.post(
             reverse('misago:admin:users:ranks:new'),
@@ -256,4 +255,4 @@ class RankAdminViewsTests(AdminTestCase):
                 'roles': [test_role_a.pk],
             })
         self.assertEqual(response.status_code, 200)
-        self.assertIn("This name collides with other rank.", response.content)
+        self.assertContains(response, "This name collides with other rank.")

+ 7 - 7
misago/users/tests/test_testutils.py

@@ -1,7 +1,7 @@
 import json
 
 from django.core.urlresolvers import reverse
-
+from django.utils.encoding import smart_str
 from ..testutils import AuthenticatedUserTestCase, SuperUserTestCase, UserTestCase
 
 
@@ -36,7 +36,7 @@ class UserTestCaseTests(UserTestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertEqual(user_json['id'], user.id)
 
     def test_login_superuser(self):
@@ -47,7 +47,7 @@ class UserTestCaseTests(UserTestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertEqual(user_json['id'], user.id)
 
     def test_logout_user(self):
@@ -59,7 +59,7 @@ class UserTestCaseTests(UserTestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])
 
     def test_logout_superuser(self):
@@ -71,7 +71,7 @@ class UserTestCaseTests(UserTestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertIsNone(user_json['id'])
 
 
@@ -79,7 +79,7 @@ class AuthenticatedUserTestCaseTests(AuthenticatedUserTestCase):
     def test_setup(self):
         """setup executed correctly"""
         response = self.client.get(reverse('misago:index'))
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
     def test_reload_user(self):
         """reload_user reloads user"""
@@ -98,5 +98,5 @@ class SuperUserTestCaseTests(SuperUserTestCase):
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
 
-        user_json = json.loads(response.content)
+        user_json = json.loads(smart_str(response.content))
         self.assertEqual(user_json['id'], self.user.id)

+ 24 - 36
misago/users/tests/test_user_avatar_api.py

@@ -4,6 +4,7 @@ from path import Path
 
 from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
+from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
 from misago.conf import settings
@@ -26,7 +27,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
             response = self.client.get(self.link)
             self.assertEqual(response.status_code, 200)
 
-            options = json.loads(response.content)
+            options = json.loads(smart_str(response.content))
             self.assertTrue(options['generated'])
             self.assertFalse(options['gravatar'])
             self.assertFalse(options['crop_org'])
@@ -40,7 +41,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
             response = self.client.get(self.link)
             self.assertEqual(response.status_code, 200)
 
-            options = json.loads(response.content)
+            options = json.loads(smart_str(response.content))
             self.assertTrue(options['generated'])
             self.assertTrue(options['gravatar'])
             self.assertFalse(options['crop_org'])
@@ -55,30 +56,26 @@ class UserAvatarTests(AuthenticatedUserTestCase):
         self.user.save()
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn('Your avatar is pwnt', response.content)
+        self.assertContains(response, 'Your avatar is pwnt', status_code=403)
 
     def test_other_user_avatar(self):
         """requests to api error if user tries to access other user"""
         self.logout_user();
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn('You have to sign in', response.content)
+        self.assertContains(response, 'You have to sign in', status_code=403)
 
         User = get_user_model()
         self.login_user(User.objects.create_user(
             "BobUser", "bob@bob.com", self.USER_PASSWORD))
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn('can\'t change other users avatars', response.content)
+        self.assertContains(response, 'can\'t change other users avatars', status_code=403)
 
     def test_empty_requests(self):
         """empty request errors with code 400"""
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Unknown avatar type.', response.content)
+        self.assertContains(response, 'Unknown avatar type.', status_code=400)
 
     def test_failed_gravatar_request(self):
         """no gravatar RPC fails"""
@@ -86,8 +83,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
         self.user.save()
 
         response = self.client.post(self.link, data={'avatar': 'gravatar'})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('No Gravatar is associated', response.content)
+        self.assertContains(response, 'No Gravatar is associated', status_code=400)
 
     def test_successful_gravatar_request(self):
         """gravatar RPC fails"""
@@ -95,14 +91,12 @@ class UserAvatarTests(AuthenticatedUserTestCase):
         self.user.save()
 
         response = self.client.post(self.link, data={'avatar': 'gravatar'})
-        self.assertEqual(response.status_code, 200)
-        self.assertIn('Gravatar was downloaded and set', response.content)
+        self.assertContains(response, 'Gravatar was downloaded and set')
 
     def test_generation_request(self):
         """generated avatar is set"""
         response = self.client.post(self.link, data={'avatar': 'generated'})
-        self.assertEqual(response.status_code, 200)
-        self.assertIn('New avatar based on your account', response.content)
+        self.assertContains(response, 'New avatar based on your account')
 
     def test_avatar_upload_and_crop(self):
         """avatar can be uploaded and cropped"""
@@ -110,11 +104,10 @@ class UserAvatarTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
 
         response = self.client.post(self.link, data={'avatar': 'upload'})
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('No file was sent.', response.content)
+        self.assertContains(response, 'No file was sent.', status_code=400)
 
         avatar_path = (settings.MEDIA_ROOT, 'avatars', 'blank.png')
-        with open('/'.join(avatar_path)) as avatar:
+        with open('/'.join(avatar_path), 'rb') as avatar:
             response = self.client.post(self.link,
                                         data={
                                             'avatar': 'upload',
@@ -122,7 +115,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
                                         })
             self.assertEqual(response.status_code, 200)
 
-            response_json = json.loads(response.content)
+            response_json = json.loads(smart_str(response.content))
             self.assertTrue(response_json['options']['crop_tmp'])
 
             avatar_dir = store.get_existing_avatars_dir(self.user)
@@ -150,10 +143,10 @@ class UserAvatarTests(AuthenticatedUserTestCase):
                     }
                 }),
                 content_type="application/json")
-            response_json = json.loads(response.content)
+            response_json = json.loads(smart_str(response.content))
 
             self.assertEqual(response.status_code, 200)
-            self.assertIn('Uploaded avatar was set.', response.content)
+            self.assertContains(response, 'Uploaded avatar was set.')
 
             avatar_dir = store.get_existing_avatars_dir(self.user)
             avatar = Path('%s/%s_tmp.png' % (avatar_dir, self.user.pk))
@@ -183,8 +176,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
                     }
                 }),
                 content_type="application/json")
-            self.assertEqual(response.status_code, 400)
-            self.assertIn('This avatar type is not allowed.', response.content)
+            self.assertContains(response, 'This avatar type is not allowed.', status_code=400)
 
             response = self.client.post(self.link, json.dumps({
                     'avatar': 'crop_org',
@@ -196,15 +188,14 @@ class UserAvatarTests(AuthenticatedUserTestCase):
                     }
                 }),
                 content_type="application/json")
-            self.assertEqual(response.status_code, 200)
-            self.assertIn('Avatar was re-cropped.', response.content)
+            self.assertContains(response, 'Avatar was re-cropped.')
 
     def test_gallery(self):
         """its possible to set avatar from gallery"""
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        options = json.loads(response.content)
+        options = json.loads(smart_str(response.content))
         self.assertTrue(options['galleries'])
 
         for gallery in options['galleries']:
@@ -214,8 +205,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
                     'image': image
                 })
 
-                self.assertEqual(response.status_code, 200)
-                self.assertIn('Avatar from gallery was set.', response.content)
+                self.assertContains(response, 'Avatar from gallery was set.')
 
 
 class UserAvatarModerationTests(AuthenticatedUserTestCase):
@@ -238,16 +228,14 @@ class UserAvatarModerationTests(AuthenticatedUserTestCase):
         })
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't moderate avatars", response.content)
+        self.assertContains(response, "can't moderate avatars", status_code=403)
 
         override_acl(self.user, {
             'can_moderate_avatars': 0,
         })
 
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't moderate avatars", response.content)
+        self.assertContains(response, "can't moderate avatars", status_code=403)
 
     def test_moderate_avatar(self):
         """moderate avatar"""
@@ -258,7 +246,7 @@ class UserAvatarModerationTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        options = json.loads(response.content)
+        options = json.loads(smart_str(response.content))
         self.assertEqual(options['is_avatar_locked'],
                          self.other_user.is_avatar_locked)
         self.assertEqual(options['avatar_lock_user_message'],
@@ -281,7 +269,7 @@ class UserAvatarModerationTests(AuthenticatedUserTestCase):
         User = get_user_model()
         other_user = User.objects.get(pk=self.other_user.pk)
 
-        options = json.loads(response.content)
+        options = json.loads(smart_str(response.content))
         self.assertEqual(other_user.is_avatar_locked, True)
         self.assertEqual(
             other_user.avatar_lock_user_message, "Test user message.")
@@ -311,7 +299,7 @@ class UserAvatarModerationTests(AuthenticatedUserTestCase):
 
         other_user = User.objects.get(pk=self.other_user.pk)
 
-        options = json.loads(response.content)
+        options = json.loads(smart_str(response.content))
         self.assertEqual(options['avatar_hash'],
                          other_user.avatar_hash)
         self.assertEqual(options['is_avatar_locked'],

+ 4 - 8
misago/users/tests/test_user_changeemail_api.py

@@ -39,8 +39,7 @@ class UserChangeEmailTests(AuthenticatedUserTestCase):
             'new_email': 'new@email.com',
             'password': 'Lor3mIpsum'
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('password is invalid', response.content)
+        self.assertContains(response, 'password is invalid', status_code=400)
 
     def test_invalid_input(self):
         """api errors correctly for invalid input"""
@@ -48,15 +47,13 @@ class UserChangeEmailTests(AuthenticatedUserTestCase):
             'new_email': '',
             'password': self.USER_PASSWORD
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('new_email":["This field is required', response.content)
+        self.assertContains(response, 'new_email":["This field is required', status_code=400)
 
         response = self.client.post(self.link, data={
             'new_email': 'newmail',
             'password': self.USER_PASSWORD
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('valid email address', response.content)
+        self.assertContains(response, 'valid email address', status_code=400)
 
     def test_email_taken(self):
         """api validates email usage"""
@@ -67,5 +64,4 @@ class UserChangeEmailTests(AuthenticatedUserTestCase):
             'new_email': 'new@email.com',
             'password': self.USER_PASSWORD
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('not available', response.content)
+        self.assertContains(response, 'not available', status_code=400)

+ 3 - 7
misago/users/tests/test_user_changepassword_api.py

@@ -39,8 +39,7 @@ class UserChangePasswordTests(AuthenticatedUserTestCase):
             'new_password': 'N3wP@55w0rd',
             'password': 'Lor3mIpsum'
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('password is invalid', response.content)
+        self.assertContains(response, 'password is invalid', status_code=400)
 
     def test_invalid_input(self):
         """api errors correctly for invalid input"""
@@ -48,13 +47,10 @@ class UserChangePasswordTests(AuthenticatedUserTestCase):
             'new_password': '',
             'password': self.USER_PASSWORD
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('new_password":["This field is required',
-                      response.content)
+        self.assertContains(response, 'new_password":["This field is required', status_code=400)
 
         response = self.client.post(self.link, data={
             'new_password': 'n',
             'password': self.USER_PASSWORD
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('password must be', response.content)
+        self.assertContains(response, 'password must be', status_code=400)

+ 11 - 15
misago/users/tests/test_user_create_api.py

@@ -28,8 +28,7 @@ class UserCreateTests(UserTestCase):
         settings.override_setting('account_activation', 'closed')
 
         response = self.client.post('/api/users/')
-        self.assertEqual(response.status_code, 403)
-        self.assertIn('closed', response.content)
+        self.assertContains(response, 'closed', status_code=403)
 
     def test_registration_creates_active_user(self):
         """api creates active and signed in user on POST"""
@@ -40,10 +39,9 @@ class UserCreateTests(UserTestCase):
                                           'email': 'bob@bob.com',
                                           'password': 'pass123'})
 
-        self.assertEqual(response.status_code, 200)
-        self.assertIn('active', response.content)
-        self.assertIn('Bob', response.content)
-        self.assertIn('bob@bob.com', response.content)
+        self.assertContains(response, 'active')
+        self.assertContains(response, 'Bob')
+        self.assertContains(response, 'bob@bob.com')
 
         User = get_user_model()
         User.objects.get_by_username('Bob')
@@ -52,7 +50,7 @@ class UserCreateTests(UserTestCase):
         self.assertEqual(Online.objects.filter(user=test_user).count(), 1)
 
         response = self.client.get(reverse('misago:index'))
-        self.assertIn('Bob', response.content)
+        self.assertContains(response, 'Bob')
 
         self.assertIn('Welcome', mail.outbox[0].subject)
 
@@ -65,10 +63,9 @@ class UserCreateTests(UserTestCase):
                                           'email': 'bob@bob.com',
                                           'password': 'pass123'})
 
-        self.assertEqual(response.status_code, 200)
-        self.assertIn('user', response.content)
-        self.assertIn('Bob', response.content)
-        self.assertIn('bob@bob.com', response.content)
+        self.assertContains(response, 'user')
+        self.assertContains(response, 'Bob')
+        self.assertContains(response, 'bob@bob.com')
 
         User = get_user_model()
         User.objects.get_by_username('Bob')
@@ -85,10 +82,9 @@ class UserCreateTests(UserTestCase):
                                           'email': 'bob@bob.com',
                                           'password': 'pass123'})
 
-        self.assertEqual(response.status_code, 200)
-        self.assertIn('admin', response.content)
-        self.assertIn('Bob', response.content)
-        self.assertIn('bob@bob.com', response.content)
+        self.assertContains(response, 'admin')
+        self.assertContains(response, 'Bob')
+        self.assertContains(response, 'bob@bob.com')
 
         User = get_user_model()
         User.objects.get_by_username('Bob')

+ 7 - 9
misago/users/tests/test_user_signature_api.py

@@ -1,6 +1,7 @@
 import json
 
 from django.contrib.auth import get_user_model
+from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
 from misago.conf import settings
@@ -23,8 +24,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         })
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("You don't have permission to change", response.content)
+        self.assertContains(response, "You don't have permission to change", status_code=403)
 
     def test_signature_locked(self):
         """locked edit signature returns 403"""
@@ -37,8 +37,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         self.user.save()
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn('Your siggy is banned', response.content)
+        self.assertContains(response, 'Your siggy is banned', status_code=403)
 
     def test_get_signature(self):
         """GET to api returns json with no signature"""
@@ -52,7 +51,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertFalse(response_json['signature'])
 
     def test_post_empty_signature(self):
@@ -67,7 +66,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link, data={'signature': ''})
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertFalse(response_json['signature'])
 
     def test_post_too_long_signature(self):
@@ -82,8 +81,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link, data={
             'signature': 'abcd' * 1000
         })
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('too long', response.content)
+        self.assertContains(response, 'too long', status_code=400)
 
     def test_post_good_signature(self):
         """POST with good signature changes user signature"""
@@ -99,7 +97,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         })
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['signature']['html'],
                          '<p>Hello, <strong>bros</strong>!</p>')
         self.assertEqual(response_json['signature']['plain'],

+ 23 - 27
misago/users/tests/test_user_username_api.py

@@ -1,6 +1,8 @@
 import json
 
 from django.contrib.auth import get_user_model
+from django.utils.encoding import smart_str
+from django.utils.six.moves import range
 
 from misago.acl.testutils import override_acl
 from misago.conf import settings
@@ -21,7 +23,7 @@ class UserUsernameTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
 
         self.assertIsNotNone(response_json['changes_left'])
         self.assertEqual(response_json['length_min'],
@@ -30,13 +32,13 @@ class UserUsernameTests(AuthenticatedUserTestCase):
                          settings.username_length_max)
         self.assertIsNone(response_json['next_on'])
 
-        for i in xrange(response_json['changes_left']):
+        for i in range(response_json['changes_left']):
             self.user.set_username('NewName%s' % i, self.user)
 
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['changes_left'], 0)
         self.assertIsNotNone(response_json['next_on'])
 
@@ -45,28 +47,26 @@ class UserUsernameTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(response.content)
-        for i in xrange(response_json['changes_left']):
+        response_json = json.loads(smart_str(response.content))
+        for i in range(response_json['changes_left']):
             self.user.set_username('NewName%s' % i, self.user)
 
         response = self.client.get(self.link)
-        response_json = json.loads(response.content)
+        response_json = json.loads(smart_str(response.content))
         self.assertEqual(response_json['changes_left'], 0)
 
         response = self.client.post(self.link, data={
             'username': 'Pointless'
         })
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('change your username now', response.content)
+        self.assertContains(response, 'change your username now', status_code=400)
         self.assertTrue(self.user.username != 'Pointless')
 
     def test_change_username_no_input(self):
         """api returns error 400 if new username is empty"""
         response = self.client.post(self.link, data={})
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('Enter new username.', response.content)
+        self.assertContains(response, 'Enter new username.', status_code=400)
 
     def test_change_username_invalid_name(self):
         """api returns error 400 if new username is wrong"""
@@ -74,13 +74,12 @@ class UserUsernameTests(AuthenticatedUserTestCase):
             'username': '####'
         })
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn('can only contain latin', response.content)
+        self.assertContains(response, 'can only contain latin', status_code=400)
 
     def test_change_username(self):
         """api changes username and records change"""
         response = self.client.get(self.link)
-        changes_left = json.loads(response.content)['changes_left']
+        changes_left = json.loads(smart_str(response.content))['changes_left']
 
         username = self.user.username
         new_username = 'NewUsernamu'
@@ -90,7 +89,7 @@ class UserUsernameTests(AuthenticatedUserTestCase):
         })
 
         self.assertEqual(response.status_code, 200)
-        options = json.loads(response.content)['options']
+        options = json.loads(smart_str(response.content))['options']
         self.assertEqual(changes_left, options['changes_left'] + 1)
 
         self.reload_user()
@@ -120,16 +119,14 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
         })
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't rename users", response.content)
+        self.assertContains(response, "can't rename users", status_code=403)
 
         override_acl(self.user, {
             'can_rename_users': 0,
         })
 
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't rename users", response.content)
+        self.assertContains(response, "can't rename users", status_code=403)
 
     def test_moderate_username(self):
         """moderate username"""
@@ -140,7 +137,7 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        options = json.loads(response.content)
+        options = json.loads(smart_str(response.content))
         self.assertEqual(options['length_min'],
                          settings.username_length_min)
         self.assertEqual(options['length_max'],
@@ -155,8 +152,7 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
             }),
             content_type="application/json")
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn("Enter new username", response.content)
+        self.assertContains(response, "Enter new username", status_code=400)
 
         override_acl(self.user, {
             'can_rename_users': 1,
@@ -167,10 +163,9 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
             }),
             content_type="application/json")
 
-        self.assertEqual(response.status_code, 400)
-        self.assertIn(
+        self.assertContains(response,
             "Username can only contain latin alphabet letters and digits.",
-            response.content)
+            status_code=400)
 
         override_acl(self.user, {
             'can_rename_users': 1,
@@ -182,8 +177,9 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
             content_type="application/json")
 
         self.assertEqual(response.status_code, 400)
-        self.assertIn(
-            "Username must be at least 3 characters long.", response.content)
+        self.assertContains(response,
+            "Username must be at least 3 characters long.",
+            status_code=400)
 
         override_acl(self.user, {
             'can_rename_users': 1,
@@ -202,7 +198,7 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
         self.assertEqual('BobBoberson', other_user.username)
         self.assertEqual('bobboberson', other_user.slug)
 
-        options = json.loads(response.content)
+        options = json.loads(smart_str(response.content))
         self.assertEqual(options['username'], other_user.username)
         self.assertEqual(options['slug'], other_user.slug)
 

+ 26 - 24
misago/users/tests/test_useradmin_views.py

@@ -3,6 +3,9 @@ import json
 from django.contrib.auth import get_user_model
 from django.core import mail
 from django.core.urlresolvers import reverse
+from django.utils import six
+from django.utils.six.moves import range
+from django.utils.encoding import smart_str
 
 from misago.acl.models import Role
 from misago.admin.testutils import AdminTestCase
@@ -17,8 +20,7 @@ class UserAdminViewsTests(AdminTestCase):
         """admin index view contains users link"""
         response = self.client.get(reverse('misago:admin:index'))
 
-        self.assertIn(reverse('misago:admin:users:accounts:index'),
-                      response.content)
+        self.assertContains(response, reverse('misago:admin:users:accounts:index'))
 
     def test_list_view(self):
         """users list view returns 200"""
@@ -28,7 +30,7 @@ class UserAdminViewsTests(AdminTestCase):
 
         response = self.client.get(response['location'])
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
     def test_list_search(self):
         """users list is searchable"""
@@ -47,27 +49,27 @@ class UserAdminViewsTests(AdminTestCase):
         # Search both
         response = self.client.get(link_base + '&username=tyr')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(user_a.username, response.content)
-        self.assertIn(user_b.username, response.content)
+        self.assertContains(response, user_a.username)
+        self.assertContains(response, user_b.username)
 
         # Search tyrion
         response = self.client.get(link_base + '&username=tyrion')
         self.assertEqual(response.status_code, 200)
-        self.assertFalse(user_a.username in response.content)
-        self.assertIn(user_b.username, response.content)
+        self.assertNotContains(response, user_a.username)
+        self.assertContains(response, user_b.username)
 
         # Search tyrael
         response = self.client.get(link_base + '&email=t123@test.com')
         self.assertEqual(response.status_code, 200)
-        self.assertIn(user_a.username, response.content)
-        self.assertFalse(user_b.username in response.content)
+        self.assertContains(response, user_a.username)
+        self.assertNotContains(response, user_b.username)
 
     def test_mass_activation(self):
         """users list activates multiple users"""
         User = get_user_model()
 
         user_pks = []
-        for i in xrange(10):
+        for i in range(10):
             test_user = User.objects.create_user(
                 'Bob%s' % i,
                 'bob%s@test.com' % i,
@@ -91,7 +93,7 @@ class UserAdminViewsTests(AdminTestCase):
         User = get_user_model()
 
         user_pks = []
-        for i in xrange(10):
+        for i in range(10):
             test_user = User.objects.create_user(
                 'Bob%s' % i,
                 'bob%s@test.com' % i,
@@ -124,7 +126,7 @@ class UserAdminViewsTests(AdminTestCase):
         User = get_user_model()
 
         user_pks = []
-        for i in xrange(10):
+        for i in range(10):
             test_user = User.objects.create_user(
                 'Bob%s' % i,
                 'bob%s@test.com' % i,
@@ -144,7 +146,7 @@ class UserAdminViewsTests(AdminTestCase):
         User = get_user_model()
 
         user_pks = []
-        for i in xrange(10):
+        for i in range(10):
             test_user = User.objects.create_user(
                 'Bob%s' % i,
                 'bob%s@test.com' % i,
@@ -171,8 +173,8 @@ class UserAdminViewsTests(AdminTestCase):
         response = self.client.post(reverse('misago:admin:users:accounts:new'),
             data={
                 'username': 'Bawww',
-                'rank': unicode(default_rank.pk),
-                'roles': unicode(authenticated_role.pk),
+                'rank': six.text_type(default_rank.pk),
+                'roles': six.text_type(authenticated_role.pk),
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'staff_level': '0'
@@ -194,8 +196,8 @@ class UserAdminViewsTests(AdminTestCase):
 
         response = self.client.post(test_link, data={
             'username': 'Bawww',
-            'rank': unicode(test_user.rank_id),
-            'roles': unicode(test_user.roles.all()[0].pk),
+            'rank': six.text_type(test_user.rank_id),
+            'roles': six.text_type(test_user.roles.all()[0].pk),
             'email': 'reg@stered.com',
             'new_password': 'pass123',
             'staff_level': '0',
@@ -223,19 +225,19 @@ class UserAdminViewsTests(AdminTestCase):
                             kwargs={'pk': test_user.pk})
 
         category = Category.objects.all_categories()[:1][0]
-        [post_thread(category, poster=test_user) for i in xrange(10)]
+        [post_thread(category, poster=test_user) for i in range(10)]
 
         response = self.client.post(test_link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
 
-        response_dict = json.loads(response.content)
+        response_dict = json.loads(smart_str(response.content))
         self.assertEqual(response_dict['deleted_count'], 10)
         self.assertFalse(response_dict['is_completed'])
 
         response = self.client.post(test_link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
 
-        response_dict = json.loads(response.content)
+        response_dict = json.loads(smart_str(response.content))
         self.assertEqual(response_dict['deleted_count'], 0)
         self.assertTrue(response_dict['is_completed'])
 
@@ -248,19 +250,19 @@ class UserAdminViewsTests(AdminTestCase):
 
         category = Category.objects.all_categories()[:1][0]
         thread = post_thread(category)
-        [reply_thread(thread, poster=test_user) for i in xrange(10)]
+        [reply_thread(thread, poster=test_user) for i in range(10)]
 
         response = self.client.post(test_link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
 
-        response_dict = json.loads(response.content)
+        response_dict = json.loads(smart_str(response.content))
         self.assertEqual(response_dict['deleted_count'], 10)
         self.assertFalse(response_dict['is_completed'])
 
         response = self.client.post(test_link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
 
-        response_dict = json.loads(response.content)
+        response_dict = json.loads(smart_str(response.content))
         self.assertEqual(response_dict['deleted_count'], 0)
         self.assertTrue(response_dict['is_completed'])
 
@@ -274,5 +276,5 @@ class UserAdminViewsTests(AdminTestCase):
         response = self.client.post(test_link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
 
-        response_dict = json.loads(response.content)
+        response_dict = json.loads(smart_str(response.content))
         self.assertTrue(response_dict['is_completed'])

+ 5 - 7
misago/users/tests/test_usernamechanges_api.py

@@ -18,7 +18,7 @@ class UsernameChangesApiTests(AuthenticatedUserTestCase):
 
         response = self.client.get('%s?user=%s' % (self.link, self.user.pk))
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
     def test_list_handles_invalid_filter(self):
         """list raises 404 for invalid filter"""
@@ -48,13 +48,13 @@ class UsernameChangesApiTests(AuthenticatedUserTestCase):
             '%s?user=%s&search=new' % (self.link, self.user.pk))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
         response = self.client.get(
             '%s?user=%s&search=usernew' % (self.link, self.user.pk))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn('[]', response.content)
+        self.assertContains(response, '[]')
 
     def test_list_denies_permission(self):
         """list denies permission for other user (or all) if no access"""
@@ -62,9 +62,7 @@ class UsernameChangesApiTests(AuthenticatedUserTestCase):
 
         response = self.client.get(
             '%s?user=%s' % (self.link, self.user.pk + 1))
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("don't have permission to", response.content)
+        self.assertContains(response, "don't have permission to", status_code=403)
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("don't have permission to", response.content)
+        self.assertContains(response, "don't have permission to", status_code=403)

+ 29 - 35
misago/users/tests/test_users_api.py

@@ -2,6 +2,7 @@ import json
 from datetime import timedelta
 
 from django.contrib.auth import get_user_model
+from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
@@ -34,11 +35,11 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
         """empty list is served"""
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(self.user.username, response.content)
+        self.assertNotContains(response, self.user.username)
 
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
-        self.assertNotIn(self.user.username, response.content)
+        self.assertNotContains(response, self.user.username)
 
     def test_filled_list(self):
         """filled list is served"""
@@ -50,18 +51,18 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
 
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
-        self.assertIn('"is_online":true', response.content)
-        self.assertIn('"is_offline":false', response.content)
+        self.assertContains(response, self.user.username)
+        self.assertContains(response, '"is_online":true')
+        self.assertContains(response, '"is_offline":false')
 
         self.logout_user()
         build_active_posters_ranking()
 
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
-        self.assertIn('"is_online":false', response.content)
-        self.assertIn('"is_offline":true', response.content)
+        self.assertContains(response, self.user.username)
+        self.assertContains(response, '"is_online":false')
+        self.assertContains(response, '"is_offline":true')
 
 
 class FollowersListTests(AuthenticatedUserTestCase):
@@ -91,7 +92,7 @@ class FollowersListTests(AuthenticatedUserTestCase):
 
         response = self.client.get(self.link % self.user.pk)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_follower.username, response.content)
+        self.assertContains(response, test_follower.username)
 
 
 class FollowsListTests(AuthenticatedUserTestCase):
@@ -121,7 +122,7 @@ class FollowsListTests(AuthenticatedUserTestCase):
 
         response = self.client.get(self.link % self.user.pk)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_follower.username, response.content)
+        self.assertContains(response, test_follower.username)
 
 
 class RankListTests(AuthenticatedUserTestCase):
@@ -163,7 +164,7 @@ class RankListTests(AuthenticatedUserTestCase):
 
         response = self.client.get(self.link % self.user.rank.pk)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
 
 class SearchNamesListTests(AuthenticatedUserTestCase):
@@ -183,7 +184,7 @@ class SearchNamesListTests(AuthenticatedUserTestCase):
         """filled list returns 200"""
         response = self.client.get(self.link + self.user.slug)
         self.assertEqual(response.status_code, 200)
-        self.assertIn(self.user.username, response.content)
+        self.assertContains(response, self.user.username)
 
 
 class UserCategoriesOptionsTests(AuthenticatedUserTestCase):
@@ -206,7 +207,7 @@ class UserCategoriesOptionsTests(AuthenticatedUserTestCase):
         )
 
         for field in fields:
-            self.assertIn('"%s"' % field, response.content)
+            self.assertContains(response, '"%s"' % field, status_code=400)
 
     def test_change_forum_options(self):
         """forum options are changed"""
@@ -243,14 +244,12 @@ class UserFollowTests(AuthenticatedUserTestCase):
         self.logout_user()
 
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("action is not available to guests", response.content)
+        self.assertContains(response, "action is not available to guests", status_code=403)
 
     def test_follow_myself(self):
         """you can't follow yourself"""
         response = self.client.post('/api/users/%s/follow/' % self.user.pk)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't add yourself to followed", response.content)
+        self.assertContains(response, "can't add yourself to followed", status_code=403)
 
     def test_cant_follow(self):
         """no permission to follow users"""
@@ -259,8 +258,7 @@ class UserFollowTests(AuthenticatedUserTestCase):
         })
 
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't follow other users", response.content)
+        self.assertContains(response, "can't follow other users", status_code=403)
 
     def test_follow(self):
         """follow and unfollow other user"""
@@ -317,8 +315,7 @@ class UserBanTests(AuthenticatedUserTestCase):
         })
 
         response = self.client.get(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't see users bans details", response.content)
+        self.assertContains(response, "can't see users bans details", status_code=403)
 
     def test_no_ban(self):
         """api returns empty json"""
@@ -328,7 +325,7 @@ class UserBanTests(AuthenticatedUserTestCase):
 
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
-        self.assertEqual(response.content, '{}')
+        self.assertEqual(smart_str(response.content), '{}')
 
     def test_ban_details(self):
         """api returns ban json"""
@@ -343,7 +340,7 @@ class UserBanTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        ban_json = json.loads(response.content)
+        ban_json = json.loads(smart_str(response.content))
         self.assertEqual(ban_json['user_message']['plain'], 'Nope!')
         self.assertEqual(ban_json['user_message']['html'], '<p>Nope!</p>')
 
@@ -380,7 +377,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
 
         response = self.client.post(self.link)
         self.assertEqual(response.status_code, 403)
-        self.assertIn("can't delete users", response.content)
+        self.assertContains(response, "can't delete users", status_code=403)
 
     def test_delete_too_many_posts(self):
         """raises 403 error when user has too many posts"""
@@ -394,7 +391,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
 
         response = self.client.post(self.link)
         self.assertEqual(response.status_code, 403)
-        self.assertIn("can't delete users", response.content)
+        self.assertContains(response, "can't delete users", status_code=403)
 
     def test_delete_too_many_posts(self):
         """raises 403 error when user has too many posts"""
@@ -408,8 +405,8 @@ class UserDeleteTests(AuthenticatedUserTestCase):
 
         response = self.client.post(self.link)
         self.assertEqual(response.status_code, 403)
-        self.assertIn("can't delete users", response.content)
-        self.assertIn("made more than 5 posts", response.content)
+        self.assertContains(response, "can't delete users", status_code=403)
+        self.assertContains(response, "made more than 5 posts", status_code=403)
 
     def test_delete_too_old_member(self):
         """raises 403 error when user is too old"""
@@ -423,8 +420,8 @@ class UserDeleteTests(AuthenticatedUserTestCase):
 
         response = self.client.post(self.link)
         self.assertEqual(response.status_code, 403)
-        self.assertIn("can't delete users", response.content)
-        self.assertIn("members for more than 5 days", response.content)
+        self.assertContains(response, "can't delete users", status_code=403)
+        self.assertContains(response, "members for more than 5 days", status_code=403)
 
     def test_delete_self(self):
         """raises 403 error when attempting to delete oneself"""
@@ -434,8 +431,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         })
 
         response = self.client.post('/api/users/%s/delete/' % self.user.pk)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't delete yourself", response.content)
+        self.assertContains(response, "can't delete yourself", status_code=403)
 
     def test_delete_admin(self):
         """raises 403 error when attempting to delete admin"""
@@ -448,8 +444,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         self.other_user.save()
 
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't delete administrators", response.content)
+        self.assertContains(response, "can't delete administrators", status_code=403)
 
     def test_delete_superadmin(self):
         """raises 403 error when attempting to delete superadmin"""
@@ -462,8 +457,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         self.other_user.save()
 
         response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertIn("can't delete administrators", response.content)
+        self.assertContains(response, "can't delete administrators", status_code=403)
 
     def test_delete_with_content(self):
         """returns 200 and deletes user with content"""

+ 7 - 8
misago/users/tests/test_warningadmin_views.py

@@ -12,8 +12,7 @@ class WarningsAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:accounts:index'))
 
         response = self.client.get(response['location'])
-        self.assertIn(reverse('misago:admin:users:warnings:index'),
-                      response.content)
+        self.assertContains(response, reverse('misago:admin:users:warnings:index'))
 
     def test_list_view(self):
         """warning levels list view returns 200"""
@@ -21,7 +20,7 @@ class WarningsAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:warnings:index'))
 
         self.assertEqual(response.status_code, 200)
-        self.assertIn('No warning levels', response.content)
+        self.assertContains(response, 'No warning levels')
 
     def test_new_view(self):
         """new warning level view has no showstoppers"""
@@ -42,7 +41,7 @@ class WarningsAdminViewsTests(AdminTestCase):
         response = self.client.get(
             reverse('misago:admin:users:warnings:index'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn('Test Level', response.content)
+        self.assertContains(response, 'Test Level')
 
     def test_edit_view(self):
         """edit warning level view has no showstoppers"""
@@ -61,7 +60,7 @@ class WarningsAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:warnings:edit',
                     kwargs={'pk': test_level.pk}))
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_level.name, response.content)
+        self.assertContains(response, test_level.name)
 
         response = self.client.post(
             reverse('misago:admin:users:warnings:edit',
@@ -78,8 +77,8 @@ class WarningsAdminViewsTests(AdminTestCase):
         response = self.client.get(
             reverse('misago:admin:users:warnings:index'))
         self.assertEqual(response.status_code, 200)
-        self.assertIn(test_level.name, response.content)
-        self.assertTrue('Test Level' not in response.content)
+        self.assertContains(response, test_level.name)
+        self.assertNotContains(response, 'Test Level')
 
     def test_move_up_view(self):
         """move warning level up view has no showstoppers"""
@@ -168,4 +167,4 @@ class WarningsAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:warnings:index'))
         self.assertEqual(response.status_code, 200)
 
-        self.assertTrue(test_level.name not in response.content)
+        self.assertNotContains(response, test_level.name)