Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility fixes for Django 1.10 compatibility, stronger tests, exclude_modeladmins option #8

Merged
merged 18 commits into from
Nov 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ env:
- DJANGO_VERSION=dj17
- DJANGO_VERSION=dj18
- DJANGO_VERSION=dj19
- DJANGO_VERSION=dj110
- DJANGO_VERSION=djdev

matrix:
Expand All @@ -21,6 +22,8 @@ matrix:
include:
- python: "2.7"
env: MODE=flake8
- python: "3.4"
env: DJANGO_VERSION=dj17

cache:
directories:
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ you can do following:
class AdminSiteSmokeTest(AdminSiteSmokeTestMixin, TestCase):
fixtures = ['data']

And you can exclude certain (external) apps with::
And you can exclude certain (external) apps or model admins with::

exclude_apps = ['constance',]
exclude_modeladmins = [apps.admin.ModelAdmin]
105 changes: 87 additions & 18 deletions django_admin_smoke_tests/tests.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import six
import django

from django.contrib import admin, auth
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied,\
ValidationError
from django.http.request import QueryDict
from django.test import TestCase
from django.test.client import RequestFactory
import sys

import six


class ModelAdminCheckException(Exception):
def __init__(self, message, original_exception):
self.original_exception = original_exception
return super(ModelAdminCheckException, self).__init__(message)


def for_all_model_admins(fn):
def test_deco(self):
for model, model_admin in self.modeladmins:
if model_admin.__class__ in self.exclude_modeladmins:
continue
if model._meta.app_label in self.exclude_apps:
continue
try:
fn(self, model, model_admin)
except Exception:
raise Exception(
"Above exception occured while running test '%s'"
except Exception as e:
six.raise_from(ModelAdminCheckException(
"Above exception occured while running test '%s' "
"on modeladmin %s (%s)" %
(fn.__name__, model_admin, model.__name__)).\
with_traceback(sys.exc_info()[2])
(fn.__name__, model_admin, model.__name__),
e), e)
return test_deco


class AdminSiteSmokeTestMixin(object):
modeladmins = None
exclude_apps = []
exclude_modeladmins = []
fixtures = ['django_admin_smoke_tests']

single_attributes = ['date_hierarchy']
Expand Down Expand Up @@ -63,22 +75,27 @@ def setUp(self):
except:
pass

def get_request(self):
request = self.factory.get('/')
def get_request(self, params=None):
request = self.factory.get('/', params)

request.user = self.superuser
return request

def post_request(self, post_data={}, params=None):
request = self.factory.post('/', params, post_data=post_data)

request.user = self.superuser
request._dont_enforce_csrf_checks = True
return request

def strip_minus(self, attr, val):
if attr in self.strip_minus_attrs and val[0] == '-':
val = val[1:]
return val

def get_fieldsets(self, model, model_admin):
request = self.get_request()
try:
return model_admin.get_fieldsets(request, obj=model())
except AttributeError:
return model_admin.declared_fieldsets
return model_admin.get_fieldsets(request, obj=model())

def get_attr_set(self, model, model_admin):
attr_set = []
Expand Down Expand Up @@ -163,8 +180,6 @@ def test_queryset(self, model, model_admin):

# TODO: use model_mommy to generate a few instances to query against
# make sure no errors happen here
if hasattr(model_admin, 'queryset'):
list(model_admin.queryset(request))
if hasattr(model_admin, 'get_queryset'):
list(model_admin.get_queryset(request))

Expand All @@ -184,8 +199,28 @@ def test_changelist_view(self, model, model_admin):
request = self.get_request()

# make sure no errors happen here
response = model_admin.changelist_view(request)
self.assertEqual(response.status_code, 200)
try:
response = model_admin.changelist_view(request)
response.render()
self.assertEqual(response.status_code, 200)
except PermissionDenied:
# this error is commonly raised by ModelAdmins that don't allow
# changelist view
pass

@for_all_model_admins
def test_changelist_view_search(self, model, model_admin):
request = self.get_request(params=QueryDict('q=test'))

# make sure no errors happen here
try:
response = model_admin.changelist_view(request)
response.render()
self.assertEqual(response.status_code, 200)
except PermissionDenied:
# this error is commonly raised by ModelAdmins that don't allow
# changelist view.
pass

@for_all_model_admins
def test_add_view(self, model, model_admin):
Expand All @@ -194,12 +229,46 @@ def test_add_view(self, model, model_admin):
# make sure no errors happen here
try:
response = model_admin.add_view(request)
if isinstance(response, django.template.response.TemplateResponse):
response.render()
self.assertEqual(response.status_code, 200)
except PermissionDenied:
# this error is commonly raised by ModelAdmins that don't allow
# adding.
pass

@for_all_model_admins
def test_change_view(self, model, model_admin):
item = model.objects.last()
if not item or model._meta.proxy:
return
pk = item.pk
request = self.get_request()

# make sure no errors happen here
response = model_admin.change_view(request, object_id=str(pk))
if isinstance(response, django.template.response.TemplateResponse):
response.render()
self.assertEqual(response.status_code, 200)

@for_all_model_admins
def test_change_post(self, model, model_admin):
item = model.objects.last()
if not item or model._meta.proxy:
return
pk = item.pk
# TODO: If we generate default post_data for post request,
# the test would be stronger
request = self.post_request()
try:
response = model_admin.change_view(request, object_id=str(pk))
if isinstance(response, django.template.response.TemplateResponse):
response.render()
self.assertEqual(response.status_code, 200)
except ValidationError:
# This the form was sent, but did not pass it's validation
pass


class AdminSiteSmokeTest(AdminSiteSmokeTestMixin, TestCase):
pass
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import setup
import django_admin_smoke_tests

from setuptools import setup

package_name = 'django_admin_smoke_tests'


Expand All @@ -17,7 +18,7 @@ def runtests():
os.environ['DJANGO_SETTINGS_MODULE'] = 'test_project.settings'
if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
django.setup()
call_command('test', 'django_admin_smoke_tests')
call_command('test', 'test_project.main.tests')
sys.exit()

setup(name='django-admin-smoke-tests',
Expand Down
48 changes: 46 additions & 2 deletions test_project/main/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# Django imports
import django

from django.contrib import admin
from django.contrib.admin import SimpleListFilter

# App imports
from .models import Channel, HasPrimarySlug, HasPrimaryUUID, Post
from .models import Channel, FailPost, ForbiddenPost,\
HasPrimarySlug, HasPrimaryUUID, Post


class ChannelAdmin(admin.ModelAdmin):
Expand All @@ -11,14 +15,33 @@ class ChannelAdmin(admin.ModelAdmin):
admin.site.register(Channel, ChannelAdmin)


class ListFilter(SimpleListFilter):
title = "list_filter"
parameter_name = "list_filter"

def __init__(self, request, params, model, model_admin):
super(ListFilter, self).__init__(request, params, model, model_admin)
self.lookup_val = request.GET.getlist('a')

def lookups(self, request, model_admin):
return ()

def queryset(self, request, queryset):
return queryset


class PostAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
list_editable = ['status']
list_display = ('title', 'author', 'status', 'modified', 'published',)
list_filter = ('author', 'status', 'channel', 'created', 'modified',
'published',)
'published', ListFilter)
readonly_fields = ['created', 'modified', 'time_diff']
ordering = ('title', '-id',)
fieldsets = [('Fielset', {
'fields': ['created', ('slug', 'title', 'author', 'status')]}),
]
date_hierarchy = 'created'

search_fields = ['title', 'text']

Expand All @@ -28,7 +51,28 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs):
return super(PostAdmin, self).formfield_for_foreignkey(db_field,
request, **kwargs)


class FailPostAdmin(admin.ModelAdmin):
search_fields = ['nonexistent_field']

if django.VERSION >= (1, 8):
list_display = ['nonexistent_field']


class ForbiddenPostAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
False

def has_change_permission(self, request, obj=None):
False

def has_delete_permission(self, request):
False


admin.site.register(Post, PostAdmin)
admin.site.register(FailPost, FailPostAdmin)
admin.site.register(ForbiddenPost, ForbiddenPostAdmin)


admin.site.register(HasPrimarySlug)
Expand Down
10 changes: 9 additions & 1 deletion test_project/main/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import uuid

# Django imports
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
from django.conf import settings


class _Abstract(models.Model):
Expand Down Expand Up @@ -83,6 +83,14 @@ def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})


class ForbiddenPost(Post):
pass


class FailPost(Post):
pass


class HasPrimarySlug(models.Model):
slug = models.SlugField(primary_key=True)
title = models.CharField(max_length=140, unique=True)
Expand Down
38 changes: 38 additions & 0 deletions test_project/main/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import django

from django.test import TestCase
from django_admin_smoke_tests.tests import AdminSiteSmokeTestMixin,\
ModelAdminCheckException, for_all_model_admins
from .admin import ChannelAdmin, FailPostAdmin, ForbiddenPostAdmin, PostAdmin


class AdminSiteSmokeTest(AdminSiteSmokeTestMixin, TestCase):
fixtures = []
exclude_apps = ['auth']
exclude_modeladmins = [FailPostAdmin, ForbiddenPostAdmin]


class FailAdminSiteSmokeTest(AdminSiteSmokeTestMixin, TestCase):
fixtures = []
exclude_modeladmins = [ForbiddenPostAdmin, PostAdmin, ChannelAdmin]

@for_all_model_admins
def test_specified_fields(self, model, model_admin):
with self.assertRaises(ModelAdminCheckException):
super(FailAdminSiteSmokeTest, self).test_specified_fields()

@for_all_model_admins
def test_changelist_view_search(self, model, model_admin):
with self.assertRaises(ModelAdminCheckException):
super(FailAdminSiteSmokeTest, self).test_changelist_view_search()

if django.VERSION >= (1, 8):
@for_all_model_admins
def test_changelist_view(self, model, model_admin):
with self.assertRaises(ModelAdminCheckException):
super(FailAdminSiteSmokeTest, self).test_changelist_view()


class ForbiddenAdminSiteSmokeTest(AdminSiteSmokeTestMixin, TestCase):
fixtures = []
exclude_modeladmins = [FailPostAdmin, PostAdmin, ChannelAdmin]
19 changes: 18 additions & 1 deletion test_project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,24 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

TEMPLATE_DEBUG = True
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'OPTIONS': {
'debug': True,
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
],
},
},
]

ALLOWED_HOSTS = []

Expand Down
Loading