diff --git a/.gitignore b/.gitignore index 9873c61..34b0fbf 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ pip-log.txt .tox nosetests.xml +# Test cache +/.pytest_cache + # Translations *.mo @@ -45,4 +48,4 @@ docs/_build output/ #PyCharm -.idea/ \ No newline at end of file +.idea/ diff --git a/.travis.yml b/.travis.yml index ddd05cb..ae8baf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,12 +4,9 @@ language: python python: - "3.5" - - "3.4" - - "2.7" env: - - DJANGO="Django==1.8.4" - - DJANGO="Django==1.9.9" - DJANGO="Django==1.10" + - DJANGO="Django==2.0.7" # command to install dependencies, e.g. pip install -r requirements.txt install: @@ -17,7 +14,7 @@ install: - pip install -r requirements-test.txt # command to run tests using coverage, e.g. python setup.py test -script: coverage run --source admin_reorder runtests.py +script: python -m pytest --cov=admin_reorder # report coverage to coveralls.io after_success: coveralls diff --git a/admin_reorder/__init__.py b/admin_reorder/__init__.py index e1424ed..73e3bb4 100644 --- a/admin_reorder/__init__.py +++ b/admin_reorder/__init__.py @@ -1 +1 @@ -__version__ = '0.3.1' +__version__ = '0.3.2' diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..929815d --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +DJANGO_SETTINGS_MODULE=tests.settings diff --git a/requirements-test.txt b/requirements-test.txt index ed7dea2..e1ec329 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -8,3 +8,6 @@ django-nose>=1.2 flake8>=2.1.0 tox>=1.7.0 +pytest==3.6.3 +pytest-django==3.3.2 +pytest-cov==2.5.1 diff --git a/runtests.py b/runtests.py deleted file mode 100644 index 396efbe..0000000 --- a/runtests.py +++ /dev/null @@ -1,52 +0,0 @@ -import sys - -try: - from django.conf import settings - - settings.configure( - DEBUG=True, - USE_TZ=True, - DATABASES={ - "default": { - "ENGINE": "django.db.backends.sqlite3", - } - }, - ROOT_URLCONF="admin_reorder.urls", - INSTALLED_APPS=[ - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sites", - "admin_reorder", - ], - SITE_ID=1, - NOSE_ARGS=['-s'], - ) - - try: - import django - setup = django.setup - except AttributeError: - pass - else: - setup() - - from django_nose import NoseTestSuiteRunner -except ImportError: - raise ImportError("To fix this error, run: pip install -r requirements-test.txt") - - -def run_tests(*test_args): - if not test_args: - test_args = ['tests'] - - # Run tests - test_runner = NoseTestSuiteRunner(verbosity=1) - - failures = test_runner.run_tests(test_args) - - if failures: - sys.exit(failures) - - -if __name__ == '__main__': - run_tests(*sys.argv[1:]) diff --git a/tests/app1/__init__.py b/tests/app1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/app1/admin.py b/tests/app1/admin.py new file mode 100644 index 0000000..90dce55 --- /dev/null +++ b/tests/app1/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin + +from tests.app1.models import App1Model1, App1Model2 + + +@admin.register(App1Model1) +class App1Model1Admin(admin.ModelAdmin): + pass + + +@admin.register(App1Model2) +class App1Model2Admin(admin.ModelAdmin): + pass diff --git a/tests/app1/models.py b/tests/app1/models.py new file mode 100644 index 0000000..31488d7 --- /dev/null +++ b/tests/app1/models.py @@ -0,0 +1,9 @@ +from django.db import models + + +class App1Model1(models.Model): + field = models.BigIntegerField() + + +class App1Model2(models.Model): + field = models.BigIntegerField() diff --git a/tests/app2/__init__.py b/tests/app2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/app2/admin.py b/tests/app2/admin.py new file mode 100644 index 0000000..39081d2 --- /dev/null +++ b/tests/app2/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin + +from tests.app2.models import App2Model1, App2Model2 + + +@admin.register(App2Model1) +class App2Model1Admin(admin.ModelAdmin): + pass + + +@admin.register(App2Model2) +class App2Model2Admin(admin.ModelAdmin): + pass diff --git a/tests/app2/models.py b/tests/app2/models.py new file mode 100644 index 0000000..b51846c --- /dev/null +++ b/tests/app2/models.py @@ -0,0 +1,9 @@ +from django.db import models + + +class App2Model1(models.Model): + field = models.BigIntegerField() + + +class App2Model2(models.Model): + field = models.BigIntegerField() diff --git a/tests/settings.py b/tests/settings.py new file mode 100644 index 0000000..744e328 --- /dev/null +++ b/tests/settings.py @@ -0,0 +1,33 @@ +DEBUG = True +USE_TZ = True +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + } +} +ROOT_URLCONF = 'admin_reorder.urls' +INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.admin', + 'django.contrib.contenttypes', + 'django.contrib.messages', + 'django.contrib.sessions', + 'django.contrib.sites', + 'tests.app1', + 'tests.app2', + 'admin_reorder', +] +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.contrib.auth.context_processors.auth', + ] + } + } +] +SITE_ID = 1 +SECRET_KEY = 'yolo' +ROOT_URLCONF = 'tests.urls' diff --git a/tests/test_middleware.py b/tests/test_middleware.py new file mode 100644 index 0000000..928a829 --- /dev/null +++ b/tests/test_middleware.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +import re +import unittest + +from django.contrib.auth.models import User +from django.core.exceptions import ImproperlyConfigured +from django.test import RequestFactory, override_settings +try: + from django.urls import resolve +except ImportError: + # Backwards compatibility with Django<1.10 + from django.core.urlresolvers import resolve +from pytest import raises, mark + +from admin_reorder.middleware import ModelAdminReorder +from tests.app1.models import App1Model1, App1Model2 +from tests.app2.models import App2Model1, App2Model2 + + +@mark.django_db() +class ModelAdminReorderTest(unittest.TestCase): + """Tests for the ModelAdminReorder middleware class.""" + def setUp(self): + self.superuser = User.objects.create(is_staff=True, is_superuser=True) + + def _make_request(self, path): + """Generates a mocked request by a staff user for a given path.""" + request = RequestFactory().get(path) + request.user = self.superuser + return request + + @staticmethod + def _get_response_for_path(request, path): + """Executes the view at path and returns the response.""" + return resolve(path).func(request) + + def _render_admin_index(self): + path = '/admin/' + request = self._make_request(path) + response = self._get_response_for_path(request, path) + + return request, response + + def test_require_configuration(self): + """ + Throw an ImproperlyConfigured error if the ADMIN_REORDER config is missing when rendering the admin. + """ + request, response = self._render_admin_index() + + with raises(ImproperlyConfigured) as exc_info: + ModelAdminReorder().process_template_response(request, response) + + assert str(exc_info.value) == 'ADMIN_REORDER config is not defined.' + + def _assert_model_prescence_in_response(self, models_to_expect, response): + """ + Given an unrendered admin index response, check that every model in the given list is mentioned there. + """ + decoded_content = response.content.decode('utf8') + + for model_to_expect in models_to_expect: + lower_case_model_name = model_to_expect.__name__.lower() + assert re.search( + ''.format(lower_case_model_name), + decoded_content, + ), lower_case_model_name + ' not present' + + def _assert_model_absence_in_response(self, models_to_be_absent, response): + """ + Given an admin index response, check that every model in the given list is missing there. + """ + decoded_content = response.content.decode('utf8') + + for model_to_be_absent in models_to_be_absent: + lower_case_model_name = model_to_be_absent.__name__.lower() + assert not re.search( + ''.format(lower_case_model_name), + decoded_content, + ), lower_case_model_name + ' not absent' + + def _render_with_configuration_and_check_model_presence(self, config, models_to_expect, models_to_be_absent=None): + """ + Renders the admin index with `config` as the ADMIN_REORDER setting, checks that certain models are + mentioned on the page, and checks that certain models are absent from the page. + """ + with override_settings(ADMIN_REORDER=config): + request, response = self._render_admin_index() + ModelAdminReorder().process_template_response(request, response) + response.render() + + # Check that the right models are present/absent + self._assert_model_prescence_in_response(models_to_expect, response) + if models_to_be_absent: + self._assert_model_absence_in_response(models_to_be_absent, response) + + def test_only_valid_apps(self): + """When the config only contains valid app names, the models should be present.""" + self._render_with_configuration_and_check_model_presence( + config=( + 'app1', + 'app2', + ), + models_to_expect=[App1Model1, App1Model2, App2Model1, App2Model2], + ) + + def test_custom_grouping(self): + """When the config only uses custom grouping, the index should render.""" + self._render_with_configuration_and_check_model_presence( + config=( + { + 'app': 'app1', + 'models': ( + 'app1.App1Model1', + 'app2.App2Model2', + ) + }, + ), + models_to_expect=[App1Model1, App2Model2], + models_to_be_absent=[App1Model2, App2Model1], + ) diff --git a/tests/test_models.py b/tests/test_models.py deleted file mode 100644 index a379a89..0000000 --- a/tests/test_models.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -test_django-modeladmin-reorder ------------- - -Tests for `django-modeladmin-reorder` models module. -""" - -import os -import shutil -import unittest - -from admin_reorder import models - - -class Testadmin_reorder(unittest.TestCase): - - def setUp(self): - pass - - def test_something(self): - pass - - def tearDown(self): - pass diff --git a/tests/urls.py b/tests/urls.py new file mode 100644 index 0000000..429c48f --- /dev/null +++ b/tests/urls.py @@ -0,0 +1,14 @@ +from django.conf.urls import url +from django.contrib import admin +from django.http import HttpResponse + + +def placeholder_view(request): + return HttpResponse('') + + +urlpatterns = [ + # Don't use path() yet for backwards compatibility in the test suite + url('^$', placeholder_view), + url('^admin/', admin.site.urls), +] diff --git a/tox.ini b/tox.ini index 8ba9665..bf83d7d 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,6 @@ envlist = py26, py27, py33 [testenv] setenv = PYTHONPATH = {toxinidir}:{toxinidir}/admin_reorder -commands = python runtests.py +commands = pytest deps = - -r{toxinidir}/requirements-test.txt \ No newline at end of file + -r{toxinidir}/requirements-test.txt