Merge pull request #24 from nephila/feature/tests

Tests
This commit is contained in:
Iacopo Spalletti 2014-06-11 13:08:15 +02:00
commit 07593e5d3d
21 changed files with 802 additions and 44 deletions

27
.coveragerc Normal file
View file

@ -0,0 +1,27 @@
[run]
branch = True
source = djangocms_blog
[report]
omit = ../*migrations*,../*tests*,../*compat.*
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
ignore_errors = True
[html]
directory = coverage_html

1
.gitignore vendored
View file

@ -43,4 +43,3 @@ lokalize*
.idea .idea
docs docs
tests

View file

@ -3,12 +3,34 @@
language: python language: python
python: python:
- "3.3" - 3.3
- "2.7" - 3.4
- "2.6" - 2.7
- 2.6
env:
matrix:
- DJANGO='django>=1.5,<1.6'
- DJANGO='django>=1.6,<1.7'
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install: pip install -r requirements-test.txt install:
- pip install $DJANGO
- pip install -r requirements-test.txt
# command to run tests, e.g. python setup.py test # command to run tests, e.g. python setup.py test
script: python runtests.py script: coverage run runtests.py
after_success: coveralls
matrix:
allow_failures:
- python: 3.3
env: DJANGO='django>=1.5,<1.6'
- python: 3.3
env: DJANGO='django>=1.6,<1.7'
- python: 3.4
env: DJANGO='django>=1.5,<1.6'
- python: 3.4
env: DJANGO='django>=1.6,<1.7'

View file

@ -14,6 +14,15 @@ A djangoCMS 3 blog application.
Still experimental and untested. You are welcome if you want to try it; if Still experimental and untested. You are welcome if you want to try it; if
you encounter any issue, please open an issue. you encounter any issue, please open an issue.
Supported Django versions:
* Django 1.5
* Django 1.6
Supported django CMS versions:
* django CMS 3.0
Documentation Documentation
------------- -------------

View file

@ -13,7 +13,7 @@ class BlogToolbar(CMSToolbar):
def populate(self): def populate(self):
if not (self.is_current_app and self.request.user.has_perm('djangocms_blog.add_post')): if not (self.is_current_app and self.request.user.has_perm('djangocms_blog.add_post')):
return return # pragma: no cover
admin_menu = self.toolbar.get_or_create_menu("djangocms_blog", _('Blog')) admin_menu = self.toolbar.get_or_create_menu("djangocms_blog", _('Blog'))
url = reverse('admin:djangocms_blog_post_changelist') url = reverse('admin:djangocms_blog_post_changelist')
admin_menu.add_modal_item(_('Post list'), url=url) admin_menu.add_modal_item(_('Post list'), url=url)
@ -21,7 +21,7 @@ class BlogToolbar(CMSToolbar):
admin_menu.add_modal_item(_('Add post'), url=url) admin_menu.add_modal_item(_('Add post'), url=url)
current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None) current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None)
if current_post and self.request.user.has_perm('djangocms_blog.change_post'): if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover
admin_menu.add_modal_item(_('Edit Post'),reverse( admin_menu.add_modal_item(_('Edit Post'),reverse(
'admin:djangocms_blog_post_change', args=(current_post.pk,)), 'admin:djangocms_blog_post_change', args=(current_post.pk,)),
active=True) active=True)

View file

@ -17,7 +17,7 @@ class TaggedFilterItem(object):
o con gli stessi tag di un model o un queryset o con gli stessi tag di un model o un queryset
""" """
tags = self._taglist(other_model, queryset) tags = self._taglist(other_model, queryset)
return self.get_query_set().filter(taglist__in=tags) return self.get_queryset().filter(taglist__in=tags)
def _taglist(self, other_model=None, queryset=None): def _taglist(self, other_model=None, queryset=None):
""" """
@ -74,6 +74,12 @@ class GenericDateTaggedManager(TaggedFilterItem, TranslationManager):
end_date_field = "date_published_end" end_date_field = "date_published_end"
publish_field = "publish" publish_field = "publish"
def get_queryset(self, *args, **kwargs):
try:
return super(GenericDateTaggedManager, self).get_queryset(*args, **kwargs)
except AttributeError:
return super(GenericDateTaggedManager, self).get_query_set(*args, **kwargs)
def published(self, queryset=None): def published(self, queryset=None):
queryset = self.published_future(queryset) queryset = self.published_future(queryset)
if self.start_date_field: if self.start_date_field:
@ -84,7 +90,7 @@ class GenericDateTaggedManager(TaggedFilterItem, TranslationManager):
def published_future(self, queryset=None): def published_future(self, queryset=None):
if queryset is None: if queryset is None:
queryset = self.get_query_set().all() queryset = self.get_queryset().all()
if self.end_date_field: if self.end_date_field:
qfilter = ( qfilter = (
models.Q(**{"%s__gte" % self.end_date_field: datetime.datetime.now()}) models.Q(**{"%s__gte" % self.end_date_field: datetime.datetime.now()})
@ -95,7 +101,7 @@ class GenericDateTaggedManager(TaggedFilterItem, TranslationManager):
def archived(self, queryset=None): def archived(self, queryset=None):
if queryset is None: if queryset is None:
queryset = self.get_query_set().all() queryset = self.get_queryset().all()
if self.end_date_field: if self.end_date_field:
qfilter = ( qfilter = (
models.Q(**{"%s__lte" % self.end_date_field: datetime.datetime.now()}) models.Q(**{"%s__lte" % self.end_date_field: datetime.datetime.now()})
@ -106,17 +112,16 @@ class GenericDateTaggedManager(TaggedFilterItem, TranslationManager):
def available(self, queryset=None): def available(self, queryset=None):
if queryset is None: if queryset is None:
queryset = self.get_query_set().all() queryset = self.get_queryset().all()
return queryset.filter(**{self.publish_field: True}) return queryset.filter(**{self.publish_field: True})
def filter_by_language(self, language): def filter_by_language(self, language):
queryset = self.get_query_set() return self.get_queryset().active_translations(language_code=language)
return queryset.filter(models.Q(language__isnull=True) | models.Q(language=language))
def get_months(self, queryset=None): def get_months(self, queryset=None):
"""Get months with aggregatet count (how much posts is in the month). Results are ordered by date.""" """Get months with aggregate count (how much posts is in the month). Results are ordered by date."""
if queryset is None: if queryset is None:
queryset = self.get_query_set() queryset = self.get_queryset()
dates = queryset.values_list(self.start_date_field, flat=True) dates = queryset.values_list(self.start_date_field, flat=True)
dates = [(x.year, x.month) for x in dates] dates = [(x.year, x.month) for x in dates]
date_counter = Counter(dates) date_counter = Counter(dates)

View file

@ -6,8 +6,9 @@ from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _, get_language from django.utils.translation import ugettext_lazy as _
from djangocms_text_ckeditor.fields import HTMLField from djangocms_text_ckeditor.fields import HTMLField
from filer.fields.image import FilerImageField from filer.fields.image import FilerImageField
from parler.models import TranslatableModel, TranslatedFields from parler.models import TranslatableModel, TranslatedFields
@ -54,7 +55,7 @@ class BlogCategory(TranslatableModel):
for lang in self.get_available_languages(): for lang in self.get_available_languages():
self.set_current_language(lang) self.set_current_language(lang)
if not self.slug and self.name: if not self.slug and self.name:
self.slug = slugify(self.name) self.slug = slugify(force_text(self.name))
self.save_translations() self.save_translations()
@ -85,7 +86,7 @@ class Post(ModelMeta, TranslatableModel):
related_name='djangocms_blog_post_full', related_name='djangocms_blog_post_full',
blank=True, null=True) blank=True, null=True)
enable_comments = models.BooleanField( enable_comments = models.BooleanField(
verbose_name = _(u'Enable comments on post'), verbose_name=_(u'Enable comments on post'),
default=settings.BLOG_ENABLE_COMMENTS default=settings.BLOG_ENABLE_COMMENTS
) )
@ -133,12 +134,12 @@ class Post(ModelMeta, TranslatableModel):
} }
def get_keywords(self): def get_keywords(self):
return self.safe_translation_getter('meta_keywords', language_code=get_language()).strip().split(",") return self.safe_translation_getter('meta_keywords').strip().split(",")
def get_description(self): def get_description(self):
description = self.safe_translation_getter('meta_description', language_code=get_language()) description = self.safe_translation_getter('meta_description', any_language=True)
if not description: if not description:
description = self.safe_translation_getter('abstract', language_code=get_language()) description = self.safe_translation_getter('abstract', any_language=True)
return description.strip() return description.strip()
def get_image_url(self): def get_image_url(self):
@ -172,7 +173,7 @@ class Post(ModelMeta, TranslatableModel):
kwargs = {'year': self.date_published.year, kwargs = {'year': self.date_published.year,
'month': self.date_published.month, 'month': self.date_published.month,
'day': self.date_published.day, 'day': self.date_published.day,
'slug': self.safe_translation_getter('slug', language_code=get_language())} 'slug': self.safe_translation_getter('slug', any_language=True)}
return reverse('djangocms_blog:post-detail', kwargs=kwargs) return reverse('djangocms_blog:post-detail', kwargs=kwargs)
def thumbnail_options(self): def thumbnail_options(self):
@ -182,7 +183,7 @@ class Post(ModelMeta, TranslatableModel):
return settings.BLOG_IMAGE_THUMBNAIL_SIZE return settings.BLOG_IMAGE_THUMBNAIL_SIZE
def full_image_options(self): def full_image_options(self):
if self.main_image_fulll_id: if self.main_image_full_id:
return self.main_image_full.as_dict return self.main_image_full.as_dict
else: else:
return settings.BLOG_IMAGE_FULL_SIZE return settings.BLOG_IMAGE_FULL_SIZE
@ -238,6 +239,7 @@ class AuthorEntriesPlugin(CMSPlugin):
def get_authors(self): def get_authors(self):
authors = self.authors.all() authors = self.authors.all()
for author in authors: for author in authors:
author.count = 0
if author.djangocms_blog_post_author.filter(publish=True).exists(): if author.djangocms_blog_post_author.filter(publish=True).exists():
author.count = author.djangocms_blog_post_author.filter(publish=True).count() author.count = author.djangocms_blog_post_author.filter(publish=True).count()
return authors return authors

View file

@ -17,7 +17,7 @@ class BaseBlogView(ViewUrlMixin):
def get_queryset(self): def get_queryset(self):
language = get_language() language = get_language()
manager = self.model._default_manager.language(language) manager = self.model._default_manager.active_translations(language_code=language)
if not self.request.user.is_staff: if not self.request.user.is_staff:
manager = manager.filter(publish=True) manager = manager.filter(publish=True)
return manager return manager

View file

@ -1,8 +1,6 @@
django>=1.5.1 -r requirements.txt
https://github.com/divio/django-cms/archive/develop.zip
coverage coverage
coveralls
mock>=1.0.1 mock>=1.0.1
nose>=1.3.0 nose>=1.3.0
django-nose>=1.2 django-nose>=1.2
# Additional test requirements go here

View file

@ -1,31 +1,119 @@
import sys import sys
from optparse import OptionParser from optparse import OptionParser
gettext = lambda s: s
try: try:
from django.conf import settings from django.conf import settings
settings.configure( settings.configure(
DEBUG=True, DEBUG=True,
THUMBNAIL_DEBUG=True,
TEMPLATE_DEBUG=True,
USE_TZ=True, USE_TZ=True,
DATABASES={ DATABASES={
"default": { 'default': {
"ENGINE": "django.db.backends.sqlite3", 'ENGINE': 'django.db.backends.sqlite3',
} }
}, },
ROOT_URLCONF="djangocms_blog.urls", TEMPLATE_CONTEXT_PROCESSORS=[
INSTALLED_APPS=[ 'django.contrib.auth.context_processors.auth',
"django.contrib.auth", 'django.contrib.messages.context_processors.messages',
"django.contrib.contenttypes", 'django.core.context_processors.i18n',
"django.contrib.sites", 'django.core.context_processors.debug',
"djangocms_blog", 'django.core.context_processors.request',
'django.core.context_processors.media',
'django.core.context_processors.csrf',
'cms.context_processors.cms_settings',
'sekizai.context_processors.sekizai',
'django.core.context_processors.static',
], ],
MIDDLEWARE_CLASSES=[
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.doc.XViewMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
'cms.middleware.language.LanguageCookieMiddleware',
'cms.middleware.user.CurrentUserMiddleware',
'cms.middleware.page.CurrentPageMiddleware',
'cms.middleware.toolbar.ToolbarMiddleware',
],
ROOT_URLCONF='tests.test_utils.urls',
INSTALLED_APPS=[
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sites',
'cms',
'django_nose',
'menus',
'mptt',
'sekizai',
'filer',
'parler',
'taggit',
'meta',
'meta_mixin',
'easy_thumbnails',
'djangocms_text_ckeditor',
'cmsplugin_filer_image',
'django_select2',
'taggit_autosuggest',
'djangocms_blog',
'tests.test_utils',
],
LANGUAGE_CODE='en',
LANGUAGES=(
('en', gettext('English')),
('fr', gettext('French')),
('it', gettext('Italiano')),
),
CMS_LANGUAGES={
1: [
{
'code': 'en',
'name': gettext('English'),
'public': True,
},
{
'code': 'it',
'name': gettext('Italiano'),
'public': True,
},
{
'code': 'fr',
'name': gettext('French'),
'public': True,
},
],
'default': {
'hide_untranslated': False,
},
},
CMS_TEMPLATES=(
('page.html', 'page'),
),
SITE_ID=1, SITE_ID=1,
NOSE_ARGS=['-s'], NOSE_ARGS=['-s'],
META_SITE_PROTOCOL='http',
META_SITE_DOMAIN='example.com',
META_USE_OG_PROPERTIES=True,
META_USE_TWITTER_PROPERTIES=True,
META_USE_GOOGLEPLUS_PROPERTIES=True,
THUMBNAIL_PROCESSORS=(
'easy_thumbnails.processors.colorspace',
'easy_thumbnails.processors.autocrop',
'filer.thumbnail_processors.scale_and_crop_with_subject_location',
'easy_thumbnails.processors.filters',
)
) )
from django_nose import NoseTestSuiteRunner from django_nose import NoseTestSuiteRunner
except ImportError: except ImportError:
raise ImportError("To fix this error, run: pip install -r requirements-test.txt") raise ImportError('To fix this error, run: pip install -r requirements-test.txt')
def run_tests(*test_args): def run_tests(*test_args):

View file

@ -45,6 +45,8 @@ setup(
'django-taggit-templatetags', 'django-taggit-templatetags',
'django-taggit-autosuggest', 'django-taggit-autosuggest',
'django-admin-enhancer', 'django-admin-enhancer',
'djangocms-text-ckeditor',
'cmsplugin-filer',
'django-meta-mixin', 'django-meta-mixin',
], ],
license="BSD", license="BSD",

125
tests/__init__.py Normal file
View file

@ -0,0 +1,125 @@
# -*- coding: utf-8 -*-
"""
Tests for `djangocms_blog` module.
"""
from cms.utils.i18n import get_language_list
from cmsplugin_filer_image.models import ThumbnailOption
from django.contrib.auth.models import User
from django.http import SimpleCookie
from django.test import TestCase, RequestFactory
from django.utils.translation import activate
from six import StringIO
from djangocms_blog.models import BlogCategory, Post
class BaseTest(TestCase):
"""
Base class with utility function
"""
request_factory = None
user = None
languages = get_language_list()
category_1 = None
thumb_1 = None
thumb_2 = None
data = {
'it': [
{'title': u'Primo post', 'abstract': u'<p>prima riga</p>',
'description': u'Questa è la descrizione', 'keywords': u'keyword1, keyword2',
'text': u'Testo del post',},
{'title': u'Secondo post', 'abstract': u'<p>prima riga del secondo post</p>',
'description': u'Descrizione del secondo post', 'keywords': u'keyword3, keyword4',
'text': u'Testo del secondo post'},
],
'en': [
{'title': u'First post', 'abstract': u'<p>first line</p>',
'description': u'This is the description', 'keywords': u'keyword1, keyword2',
'text': u'Post text'},
{'title': u'Second post', 'abstract': u'<p>second post first line</p>',
'description': u'Second post description', 'keywords': u'keyword3, keyword4',
'text': u'Second post text'}
]
}
@classmethod
def setUpClass(cls):
cls.request_factory = RequestFactory()
cls.user = User.objects.create(username='admin', is_staff=True, is_superuser=True)
cls.user_staff = User.objects.create(username='staff', is_staff=True)
cls.user_normal = User.objects.create(username='normal')
def setUp(self):
activate('en')
super(BaseTest, self).setUp()
self.category_1 = BlogCategory.objects.create()
self.category_1.name = u'category 1'
self.category_1.save()
self.category_1.set_current_language('it')
self.category_1.name = u'categoria 1'
self.category_1.save()
self.category_1.set_current_language('en')
self.thumb_1 = ThumbnailOption.objects.create(
name='base', width=100, height=100, crop=True, upscale=False
)
self.thumb_2 = ThumbnailOption.objects.create(
name='main', width=200, height=200, crop=False, upscale=False
)
def _get_post(self, data, post=None, lang='en'):
if not post:
post = Post()
post.set_current_language(lang)
post.author = self.user
post.title = data['title']
post.abstract = data['abstract']
post.meta_description = data['description']
post.meta_keywords = data['keywords']
post.save()
post.categories.add(self.category_1)
post.save()
return post
@classmethod
def tearDownClass(cls):
User.objects.all().delete()
def get_pages(self):
from cms.api import create_page, create_title
page = create_page(u'page one', 'page.html', language='en')
page_2 = create_page(u'page two', 'page.html', language='en')
create_title(language='fr', title=u'page un', page=page)
create_title(language='it', title=u'pagina uno', page=page)
for lang in self.languages:
page.publish(lang)
page_2.publish('en')
return page.get_draft_object(), page_2.get_draft_object()
def get_request(self, page, lang):
request = self.request_factory.get(page.get_path(lang))
request.current_page = page
request.user = self.user
request.session = {}
request.cookies = SimpleCookie()
request.errors = StringIO()
return request
def get_page_request(self, page, user, path=None, edit=False, lang_code='en'):
from cms.middleware.toolbar import ToolbarMiddleware
path = path or page and page.get_absolute_url()
if edit:
path += '?edit'
request = RequestFactory().get(path)
request.session = {}
request.user = user
request.LANGUAGE_CODE = lang_code
if edit:
request.GET = {'edit': None}
else:
request.GET = {'edit_off': None}
request.current_page = page
mid = ToolbarMiddleware()
mid.process_request(request)
return request

203
tests/test_models.py Normal file
View file

@ -0,0 +1,203 @@
# -*- coding: utf-8 -*-
from cms.api import add_plugin
from cms.utils.copy_plugins import copy_plugins_to
from cms.utils.plugins import downcast_plugins
from datetime import date
from django.core.urlresolvers import reverse
import parler
from taggit.models import Tag
from djangocms_blog.models import Post
from djangocms_blog import settings
from . import BaseTest
class ModelsTest(BaseTest):
def test_model_attributes(self):
post = self._get_post(self.data['en'][0])
post = self._get_post(self.data['it'][0], post, 'it')
post.set_current_language('en')
meta_en = post.as_meta()
self.assertEqual(meta_en.og_type, settings.BLOG_FB_TYPE)
self.assertEqual(meta_en.title, post.title)
self.assertTrue(meta_en.url.endswith(post.get_absolute_url()))
self.assertEqual(meta_en.description, post.meta_description)
self.assertEqual(meta_en.keywords, post.meta_keywords.split(','))
self.assertEqual(meta_en.published_time, post.date_published)
post.set_current_language('it')
meta_it = post.as_meta()
self.assertEqual(meta_it.title, post.title)
self.assertTrue(meta_it.url.endswith(post.get_absolute_url()))
self.assertNotEqual(meta_it.title, meta_en.title)
self.assertEqual(meta_it.description, post.meta_description)
post.set_current_language('en')
kwargs = {'year': post.date_published.year,
'month': post.date_published.month,
'day': post.date_published.day,
'slug': post.safe_translation_getter('slug', any_language=True)}
url_en = reverse('djangocms_blog:post-detail', kwargs=kwargs)
self.assertEqual(url_en, post.get_absolute_url())
post.set_current_language('it')
kwargs = {'year': post.date_published.year,
'month': post.date_published.month,
'day': post.date_published.day,
'slug': post.safe_translation_getter('slug', any_language=True)}
url_it = reverse('djangocms_blog:post-detail', kwargs=kwargs)
self.assertEqual(url_it, post.get_absolute_url())
self.assertNotEqual(url_it, url_en)
self.assertEqual(post.get_full_url(), 'http://example.com%s' % url_it)
self.assertEqual(post.thumbnail_options(), settings.BLOG_IMAGE_THUMBNAIL_SIZE)
self.assertEqual(post.full_image_options(), settings.BLOG_IMAGE_FULL_SIZE)
post.main_image_thumbnail = self.thumb_1
post.main_image_full = self.thumb_2
self.assertEqual(post.thumbnail_options(), {
'size': (100, 100),
'width': 100, 'height': 100,
'crop': True,
'upscale': False
})
self.assertEqual(post.full_image_options(), {
'size': (200, 200),
'width': 200, 'height': 200,
'crop': False,
'upscale': False
})
def test_manager(self):
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
# default queryset, published and unpublished posts
months = Post.objects.get_months()
for data in months:
self.assertEqual(data['date'], date(year=date.today().year, month=date.today().month, day=1))
self.assertEqual(data['count'], 2)
# custom queryset, only published
post_1.publish = True
post_1.save()
months = Post.objects.get_months(Post.objects.published())
for data in months:
self.assertEqual(data['date'], date(year=date.today().year, month=date.today().month, day=1))
self.assertEqual(data['count'], 1)
self.assertEqual(len(Post.objects.available()), 1)
# If post is published but publishing date is in the future
post_2.date_published = date(year=date.today().year+1, month=date.today().month, day=1)
post_2.publish = True
post_2.save()
self.assertEqual(len(Post.objects.available()), 2)
self.assertEqual(len(Post.objects.published()), 1)
self.assertEqual(len(Post.objects.archived()), 0)
# If post is published but end publishing date is in the past
post_2.date_published = date(year=date.today().year-2, month=date.today().month, day=1)
post_2.date_published_end = date(year=date.today().year-1, month=date.today().month, day=1)
post_2.save()
self.assertEqual(len(Post.objects.available()), 2)
self.assertEqual(len(Post.objects.published()), 1)
self.assertEqual(len(Post.objects.archived()), 1)
# counting with language fallback enabled
post = self._get_post(self.data['it'][0], post_1, 'it')
self.assertEqual(len(Post.objects.filter_by_language('it')), 2)
# No fallback
parler.appsettings.PARLER_LANGUAGES['default']['hide_untranslated'] = True
self.assertEqual(len(Post.objects.filter_by_language('it')), 1)
parler.appsettings.PARLER_LANGUAGES['default']['hide_untranslated'] = False
def test_tag_cloud(self):
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
post_1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post_1.save()
post_2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
post_2.save()
self.assertEqual(len(Post.objects.tag_cloud()), 0)
tags = []
for tag in Tag.objects.all():
if tag.slug == 'tag-2':
tag.count = 2
else:
tag.count = 1
tags.append(tag)
self.assertEqual(Post.objects.tag_cloud(published=True), [])
self.assertEqual(set(Post.objects.tag_cloud(published=False)), set(tags))
tags_1 = []
for tag in Tag.objects.all():
if tag.slug == 'tag-2':
tag.count = 2
tags_1.append(tag)
elif tag.slug in ('tag-1', 'tag-3', 'tag-4'):
tag.count = 1
tags_1.append(tag)
post_1.publish = True
post_1.save()
self.assertEqual(set(Post.objects.tag_cloud()), set(tags_1))
self.assertEqual(set(Post.objects.tag_cloud(published=False)), set(tags))
def test_plugin_latest(self):
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
post_1.tags.add('tag 1')
post_1.save()
plugin = add_plugin(post_1.content, 'BlogLatestEntriesPlugin', language='en')
tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag)
self.assertEqual(len(plugin.get_posts()), 0)
post_1.publish = True
post_1.save()
self.assertEqual(len(plugin.get_posts()), 1)
def test_copy_plugin_latest(self):
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
tag = Tag.objects.create(name='tag 1')
plugin = add_plugin(post_1.content, 'BlogLatestEntriesPlugin', language='en')
plugin.tags.add(tag)
plugins = list(post_1.content.cmsplugin_set.filter(language='en').order_by('tree_id', 'level', 'position'))
copy_plugins_to(plugins, post_2.content)
new = downcast_plugins(post_2.content.cmsplugin_set.all())
self.assertEqual(set(new[0].tags.all()), set([tag]))
def test_plugin_author(self):
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
plugin = add_plugin(post_1.content, 'BlogAuthorPostsPlugin', language='en')
plugin.authors.add(self.user)
self.assertEqual(len(plugin.get_posts()), 0)
self.assertEqual(plugin.get_authors()[0].count, 0)
post_1.publish = True
post_1.save()
self.assertEqual(len(plugin.get_posts()), 1)
self.assertEqual(plugin.get_authors()[0].count, 1)
post_2.publish = True
post_2.save()
self.assertEqual(len(plugin.get_posts()), 2)
self.assertEqual(plugin.get_authors()[0].count, 2)
def test_copy_plugin_author(self):
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
plugin = add_plugin(post_1.content, 'BlogAuthorPostsPlugin', language='en')
plugin.authors.add(self.user)
plugins = list(post_1.content.cmsplugin_set.filter(language='en').order_by('tree_id', 'level', 'position'))
copy_plugins_to(plugins, post_2.content)
new = downcast_plugins(post_2.content.cmsplugin_set.all())
self.assertEqual(set(new[0].authors.all()), set([self.user]))

72
tests/test_plugins.py Normal file
View file

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
import re
from cms.api import add_plugin
from django.core.urlresolvers import reverse
from django.template import RequestContext
from taggit.models import Tag
from . import BaseTest
class PluginTest(BaseTest):
def test_plugin_latest(self):
page1, page2 = self.get_pages()
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
post_1.tags.add('tag 1')
post_1.publish = True
post_1.save()
ph = page1.placeholders.get(slot='placeholder')
plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en')
tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag)
request = self.get_page_request(page1, self.user, r'/en/blog/', lang_code='en', edit=True)
context = RequestContext(request, {})
rendered = plugin.render_plugin(context, ph)
self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1)
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1)
self.assertTrue(rendered.find('<p>first line</p>') > -1)
self.assertTrue(rendered.find('<article id="post-first-post"') > -1)
self.assertTrue(rendered.find(post_1.get_absolute_url()) > -1)
def test_plugin_authors(self):
page1, page2 = self.get_pages()
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
post_1.publish = True
post_1.save()
post_2.publish = True
post_2.save()
ph = page1.placeholders.get(slot='placeholder')
plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en')
plugin.authors.add(self.user)
request = self.get_page_request(page1, self.user, r'/en/blog/', lang_code='en', edit=True)
context = RequestContext(request, {})
rendered = plugin.render_plugin(context, ph)
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-author', kwargs={'username': self.user.username})) > -1)
self.assertTrue(rendered.find('2 articles') > -1)
def test_plugin_tags(self):
page1, page2 = self.get_pages()
post_1 = self._get_post(self.data['en'][0])
post_2 = self._get_post(self.data['en'][1])
post_1.tags.add('tag 1', 'tag 2', 'test tag')
post_1.publish = True
post_1.save()
post_2.tags.add('test tag', 'another tag')
post_2.publish = True
post_2.save()
ph = page1.placeholders.get(slot='placeholder')
plugin = add_plugin(ph, 'BlogTagsPlugin', language='en')
request = self.get_page_request(page1, self.user, r'/en/blog/', lang_code='en', edit=True)
context = RequestContext(request, {})
rendered = plugin.render_plugin(context, ph).replace("\n", "")
for tag in Tag.objects.all():
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1)
if tag.slug == 'test-tag':
rf = '\s+%s\s+<span>\(\s+%s articles' % (tag.name, 2)
else:
rf = '\s+%s\s+<span>\(\s+%s article' % (tag.name, 1)
rx = re.compile(rf)
self.assertEqual(len(rx.findall(rendered)), 1)

27
tests/test_toolbar.py Normal file
View file

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from cms.toolbar.items import ModalItem
from django.core.urlresolvers import reverse
from djangocms_blog.models import BLOG_CURRENT_POST_IDENTIFIER
from . import BaseTest
class ToolbarTest(BaseTest):
def test_toolbar_with_items(self):
"""
Test that Blog toolbar is present and contains all items
"""
from cms.toolbar.toolbar import CMSToolbar
post = self._get_post(self.data['en'][0])
page1, page2 = self.get_pages()
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True)
setattr(request, BLOG_CURRENT_POST_IDENTIFIER, post)
toolbar = CMSToolbar(request)
toolbar.get_left_items()
blog_menu = toolbar.menus['djangocms_blog']
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_changelist'))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_add'))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(post.pk,)))), 1)

View file

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _

View file

@ -0,0 +1,6 @@
{% include "page.html" %}
{% block content %}
<div class="app app-blog span8">
{% block content_blog %}{% endblock %}
</div>
{% endblock content %}

View file

@ -0,0 +1,9 @@
{% load cms_tags sekizai_tags %}
{% render_block "css" %}
{% cms_toolbar %}
{% block content %}
{% placeholder "placeholder" %}
{% endblock content %}
{% render_block "js" %}

27
tests/test_utils/urls.py Normal file
View file

@ -0,0 +1,27 @@
from cms.utils.conf import get_cms_setting
from django.conf import settings
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.conf.urls.i18n import i18n_patterns
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
admin.autodiscover()
urlpatterns = patterns('',
#(r'', include('django.contrib.staticfiles.urls')),
url(r'^media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
url(r'^media/cms/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': get_cms_setting('MEDIA_ROOT'), 'show_indexes': True}),
url(r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog'),
)
urlpatterns += staticfiles_urlpatterns()
urlpatterns += i18n_patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')),
url(r'^', include('cms.urls')),
)

117
tests/test_views.py Normal file
View file

@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
from datetime import date
from django.contrib.auth.models import AnonymousUser
from django.http import Http404
from django.utils.translation import activate
from djangocms_blog.views import PostListView, PostDetailView, PostArchiveView
from . import BaseTest
class ViewTest(BaseTest):
def test_post_list_view(self):
page1, page2 = self.get_pages()
post_1 = self._get_post(self.data['en'][0])
post_1 = self._get_post(self.data['it'][0], post_1, 'it')
post_1.publish = True
post_1.save()
post_1.set_current_language('en')
post_2 = self._get_post(self.data['en'][1])
post_2 = self._get_post(self.data['it'][1], post_2, 'it')
post_2.set_current_language('en')
request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False)
activate('en')
view_obj = PostListView()
view_obj.request = request
self.assertEqual(list(view_obj.get_queryset()), [post_1])
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=False)
view_obj.request = request
self.assertEqual(set(view_obj.get_queryset()), set([post_1, post_2]))
view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset()
view_obj.paginate_by = 1
context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post_2])
self.assertEqual(context['paginator'].count, 2)
self.assertEqual(context['post_list'][0].title, 'Second post')
response = view_obj.render_to_response(context)
self.assertContains(response, context['post_list'][0].get_absolute_url())
request = self.get_page_request(page1, self.user, r'/it/blog/', lang_code='it', edit=False)
activate('it')
view_obj.request = request
view_obj.object_list = view_obj.get_queryset()
context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertEqual(context['post_list'][0].title, 'Secondo post')
response = view_obj.render_to_response(context)
self.assertContains(response, context['post_list'][0].get_absolute_url())
def test_post_detail_view(self):
page1, page2 = self.get_pages()
post_1 = self._get_post(self.data['en'][0])
post_1 = self._get_post(self.data['it'][0], post_1, 'it')
post_1.publish = True
post_1.save()
post_1.set_current_language('en')
post_2 = self._get_post(self.data['en'][1])
post_2 = self._get_post(self.data['it'][1], post_2, 'it')
post_2.set_current_language('en')
request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False)
activate('en')
view_obj = PostDetailView()
view_obj.request = request
with self.assertRaises(Http404):
view_obj.kwargs = {'slug': 'not-existing'}
post_obj = view_obj.get_object()
view_obj.kwargs = {'slug': post_1.slug}
post_obj = view_obj.get_object()
self.assertEqual(post_obj, post_1)
self.assertEqual(post_obj.language_code, 'en')
request = self.get_page_request(page1, AnonymousUser(), r'/it/blog/', lang_code='it', edit=False)
activate('it')
view_obj.request = request
post_obj = view_obj.get_object()
self.assertEqual(post_obj, post_1)
self.assertEqual(post_obj.language_code, 'it')
view_obj.object = post_obj
context = view_obj.get_context_data()
self.assertEqual(context['post'], post_1)
self.assertEqual(context['post'].language_code, 'it')
self.assertTrue(context['meta'])
def test_post_archive_view(self):
page1, page2 = self.get_pages()
post_1 = self._get_post(self.data['en'][0])
post_1 = self._get_post(self.data['it'][0], post_1, 'it')
post_1.publish = True
post_1.save()
post_1.set_current_language('en')
post_2 = self._get_post(self.data['en'][1])
post_2 = self._get_post(self.data['it'][1], post_2, 'it')
post_2.set_current_language('en')
request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False)
activate('en')
view_obj = PostArchiveView()
view_obj.request = request
view_obj.kwargs = {'year': date.today().year, 'month': date.today().month}
# One post only, anonymous request
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 1)
self.assertEqual(list(qs), [post_1])
view_obj.object_list = qs
context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertEqual(context['archive_date'], date(year=date.today().year, month=date.today().month, day=1))

30
tox.ini
View file

@ -1,10 +1,28 @@
[tox] [tox]
envlist = py26, py27, py33 envlist = py26dj15, py26dj16, py27dj15, py27dj16
[testenv] [testenv]
setenv = commands = python runtests.py
PYTHONPATH = {toxinidir}:{toxinidir}/djangocms_blog
commands = python setup.py test
deps = deps =
-r{toxinidir}/requirements-dev.txt -r{toxinidir}/requirements-test.txt
-r{toxinidir}/requirements.txt
[testenv:py26dj15]
deps =
django<1.6
{[testenv]deps}
[testenv:py26dj16]
deps =
django<1.7
{[testenv]deps}
[testenv:py27dj15]
deps =
django<1.6
{[testenv]deps}
[testenv:py27dj16]
deps =
django<1.7
{[testenv]deps}