Merge branch 'master' into scheduled-publishing

Conflicts:
	wagtail/wagtailadmin/tests.py
	wagtail/wagtailcore/tests.py
This commit is contained in:
Karl Hobley 2014-06-03 10:46:57 +01:00
commit 610ac3fc4b
18 changed files with 1166 additions and 143 deletions

View file

@ -13,7 +13,7 @@ services:
install:
- python setup.py install
- pip install psycopg2 pyelasticsearch elasticutils==0.8.2 wand
- pip install coveralls unittest2
- pip install coveralls
# Pre-test configuration
before_script:
- psql -c 'create database wagtaildemo;' -U postgres

View file

@ -1,7 +1,7 @@
.. image:: https://travis-ci.org/torchbox/wagtail.png?branch=master
:target: https://travis-ci.org/torchbox/wagtail
.. image:: https://coveralls.io/repos/torchbox/wagtail/badge.png?branch=master
.. image:: https://coveralls.io/repos/torchbox/wagtail/badge.png?branch=master&zxcv
:target: https://coveralls.io/r/torchbox/wagtail?branch=master
.. image:: https://pypip.in/v/wagtail/badge.png?zxcv
@ -24,7 +24,7 @@ Wagtail is a Django content management system built originally for the `Royal Co
* Support for tree-based content organisation
* Optional preview->submit->approve workflow
* Fast out of the box. `Varnish <https://www.varnish-cache.org/>`_-friendly if you need it
* Tests! But not enough; we're working hard to improve this
* Excellent test coverage
Find out more at `wagtail.io <http://wagtail.io/>`_.

View file

@ -1,58 +1,81 @@
For Front End developers
========================
.. note::
This documentation is currently being written.
.. contents::
========================
Overview
========================
This page is aimed at non-Django-literate Front End developers.
Wagtail uses Django's templating language. For developers new to Django, start with Django's own template documentation:
https://docs.djangoproject.com/en/dev/topics/templates/
Python programmers new to Django/Wagtail may prefer more technical documentation:
https://docs.djangoproject.com/en/dev/ref/templates/api/
You should be familiar with Django templating basics before continuing with this documentation.
==========================
Displaying Pages
Templates
==========================
Template Location
~~~~~~~~~~~~~~~~~
Every type of page or "content type" in Wagtail is defined as a "model" in a file called ``models.py``. If your site has a blog, you might have a ``BlogPage`` model and another called ``BlogPageListing``. The names of the models are up to the Django developer.
For each of your ``Page``-derived models, Wagtail will look for a template in the following location, relative to your project root::
For each page model in ``models.py``, Wagtail assumes an HTML template file exists of (almost) the same name. The Front End developer may need to create these templates themselves by refering to ``models.py`` to infer template names from the models defined therein.
project/
app/
To find a suitable template, Wagtail converts CamelCase names to underscore_case. So for a ``BlogPage``, a template ``blog_page.html`` will be expected. The name of the template file can be overridden per model if necessary.
Template files are assumed to exist here::
name_of_project/
name_of_app/
templates/
app/
blog_index_page.html
name_of_app/
blog_page.html
models.py
Class names are converted from camel case to underscores. For example, the template for model class ``BlogIndexPage`` would be assumed to be ``blog_index_page.html``. For more information, see the Django documentation for the `application directories template loader`_.
For more information, see the Django documentation for the `application directories template loader`_.
.. _application directories template loader: https://docs.djangoproject.com/en/dev/ref/templates/api/
Self
~~~~
Page content
~~~~~~~~~~~~
By default, the context passed to a model's template consists of two properties: ``self`` and ``request``. ``self`` is the model object being displayed. ``request`` is the normal Django request object. So, to include the title of a ``Page``, use ``{{ self.title }}``.
The data/content entered into each page is accessed/output through Django's ``{{ double-brace }}`` notation. Each field from the model must be accessed by prefixing ``self.``. e.g the page title ``{{ self.title }}`` or another field ``{{ self.author }}``.
========================
Static files (css, js, images)
========================
Additionally ``request.`` is available and contains Django's request object.
==============
Static assets
==============
Images
~~~~~~
Static files e.g CSS, JS and images are typically stored here::
name_of_project/
name_of_app/
static/
name_of_app/
css/
js/
images/
models.py
Images uploaded to Wagtail go into the image library and from there are added to pages via the :doc:`page editor interface </editor_manual/new_pages/inserting_images>`.
(The names "css", "js" etc aren't important, only their position within the tree.)
Any file within the static folder should be inserted into your HTML using the ``{% static %}`` tag. More about it: :ref:`static_tag`.
User images
~~~~~~~~~~~
Images uploaded to Wagtail by its users (as opposed to a developer's static files, above) go into the image library and from there are added to pages via the :doc:`page editor interface </editor_manual/new_pages/inserting_images>`.
Unlike other CMS, adding images to a page does not involve choosing a "version" of the image to use. Wagtail has no predefined image "formats" or "sizes". Instead the template developer defines image manipulation to occur *on the fly* when the image is requested, via a special syntax within the template.
Images from the library **must** be requested using this syntax, but images in your codebase can be added via conventional means e.g ``img`` tags. Only images from the library can be manipulated on the fly.
Images from the library **must** be requested using this syntax, but a developer's static images can be added via conventional means e.g ``img`` tags. Only images from the library can be manipulated on the fly.
Read more about the image manipulation syntax here :ref:`image_tag`.
@ -69,20 +92,27 @@ In addition to Django's standard tags and filters, Wagtail provides some of it's
Images (tag)
~~~~~~~~~~~~
The syntax for displaying/manipulating an image is thus::
The ``image`` tag inserts an XHTML-compatible ``img`` element into the page, setting its ``src``, ``width``, ``height`` and ``alt``. See also :ref:`image_tag_alt`.
The syntax for the tag is thus::
{% image [image] [method]-[dimension(s)] %}
For example::
For example:
.. code-block:: django
{% load image %}
...
{% image self.photo width-400 %}
<!-- or a square thumbnail: -->
{% image self.photo fill-80x80 %}
The ``image`` is the Django object refering to the image. If your page model defined a field called "photo" then ``image`` would probably be ``self.photo``. The ``method`` defines which resizing algorithm to use and ``dimension(s)`` provides height and/or width values (as ``[width|height]`` or ``[width]x[height]``) to refine that algorithm.
In the above syntax ``[image]`` is the Django object refering to the image. If your page model defined a field called "photo" then ``[image]`` would probably be ``self.photo``. The ``[method]`` defines which resizing algorithm to use and ``[dimension(s)]`` provides height and/or width values (as ``[width|height]`` or ``[width]x[height]``) to refine that algorithm.
Note that a space separates ``image`` and ``method``, but not ``method`` and ``dimensions``: a hyphen between ``method`` and ``dimensions`` is mandatory. Multiple dimensions must be separated by an ``x``.
Note that a space separates ``[image]`` and ``[method]``, but not ``[method]`` and ``[dimensions]``: a hyphen between ``[method]`` and ``[dimensions]`` is mandatory. Multiple dimensions must be separated by an ``x``.
The available ``method`` s are:
@ -123,53 +153,85 @@ The available ``method`` s are:
.. Note::
Wagtail *does not allow deforming or stretching images*. Image dimension ratios will always be kept. Wagtail also *does not support upscaling*. Small images forced to appear at larger sizes will "max out" at their their native dimensions.
.. Note::
Wagtail does not make the "original" version of an image explicitly available. To request it, it's suggested you rely on the lack of upscaling by requesting an image much larger than it's maximum dimensions. e.g to insert an image who's dimensions are uncertain/unknown at it's maximum size, try: ``{% image self.image width-10000 %}``. This assumes the image is unlikely to be larger than 10000px wide.
.. _image_tag_alt:
More control over the ``img`` tag
---------------------------------
In some cases greater control over the ``img`` tag is required, for example to add a custom ``class``. Rather than generating the ``img`` element for you, Wagtail can assign the relevant data to another object using Django's ``as`` syntax:
.. code-block:: django
{% load image %}
...
{% image self.photo width-400 as tmp_photo %}
<img src="{{ tmp_photo.src }}" width="{{ tmp_photo.width }}"
height="{{ tmp_photo.height }}" alt="{{ tmp_photo.alt }}" class="my-custom-class" />
To request the "original" version of an image, it is suggested you rely on the lack of upscaling support by requesting an image much larger than it's maximum dimensions. e.g to insert an image who's dimensions are uncertain/unknown, at it's maximum size, try: ``{% image self.image width-10000 %}``. This assumes the image is unlikely to be larger than 10000px wide.
.. _rich-text-filter:
Rich text (filter)
~~~~~~~~~~~~~~~~~~
This filter is required for use with any ``RichTextField``. It will expand internal shorthand references to embeds and links made in the Wagtail editor into fully-baked HTML ready for display. **Note that the template tag loaded differs from the name of the filter.**
This filter takes a chunk of HTML content and renders it as safe HTML in the page. Importantly it also expands internal shorthand references to embedded images and links made in the Wagtail editor into fully-baked HTML ready for display.
Only fields using ``RichTextField`` need this applied in the template.
.. code-block:: django
{% load rich_text %}
...
{{ body|richtext }}
{{ self.body|richtext }}
.. Note::
Note that the template tag loaded differs from the name of the filter.
Internal links (tag)
~~~~~~~~~~~~~~~~~~~~
**pageurl**
pageurl
--------
Takes a ``Page``-derived object and returns its URL as relative (``/foo/bar/``) if it's within the same site as the current page, or absolute (``http://example.com/foo/bar/``) if not.
Takes a Page object and returns a relative URL (``/foo/bar/``) if within the same site as the current page, or absolute (``http://example.com/foo/bar/``) if not.
.. code-block:: django
{% load pageurl %}
...
<a href="{% pageurl blog %}">
<a href="{% pageurl self.blog_page %}">
**slugurl**
slugurl
--------
Takes a ``slug`` string and returns the URL for the ``Page``-derived object with that slug. Like ``pageurl``, will try to provide a relative link if possible, but will default to an absolute link if on a different site.
Takes any ``slug`` as defined in a page's "Promote" tab and returns the URL for the matching Page. Like ``pageurl``, will try to provide a relative link if possible, but will default to an absolute link if on a different site. This is most useful when creating shared page furniture e.g top level navigation or site-wide links.
.. code-block:: django
{% load slugurl %}
...
<a href="{% slugurl blogslug %}">
<a href="{% slugurl self.your_slug %}">
.. _static_tag:
Static files (tag)
~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~
Used to load anything from your static files directory. Use of this tag avoids rewriting all static paths if hosting arrangements change, as they might between local and a live environments.
Misc
~~~~~~~~~~
.. code-block:: django
{% load static %}
...
<img src="{% static "name_of_app/myimage.jpg" %}" alt="My image"/>
Notice that the full path name is not required and the path snippet you enter only need begin with the parent app's directory name.
@ -177,10 +239,19 @@ Misc
Wagtail User Bar
========================
This tag provides a Wagtail icon and flyout menu on the top-right of a page for a logged-in user with editing capabilities, with the option of editing the current Page-derived object or adding a new sibling object.
This tag provides a contextual flyout menu on the top-right of a page for logged-in users. The menu gives editors the ability to edit the current page or add another at the same level. Moderators are also given the ability to accept or reject a page previewed as part of content moderation.
.. code-block:: django
{% load wagtailuserbar %}
...
{% wagtailuserbar %}
By default the User Bar appears in the top right of the browser window, flush with the edge. If this conflicts with your design it can be moved with a css rule in your own CSS files e.g to move it down from the top:
.. code-block:: css
#wagtail-userbar{
top:200px
}

View file

@ -1,7 +1,3 @@
# Requirements essential for developing wagtail (not needed to run it)
unittest2==0.5.1
# For coverage and PEP8 linting
coverage==3.7.1
flake8==2.1.0

View file

@ -39,7 +39,7 @@
"model": "wagtailcore.page",
"fields": {
"title": "Events",
"numchild": 2,
"numchild": 3,
"show_in_menus": true,
"live": true,
"depth": 3,
@ -62,7 +62,7 @@
"model": "wagtailcore.page",
"fields": {
"title": "Christmas",
"numchild": 1,
"numchild": 0,
"show_in_menus": true,
"live": true,
"depth": 4,
@ -90,7 +90,7 @@
"model": "wagtailcore.page",
"fields": {
"title": "Tentative Unpublished Event",
"numchild": 1,
"numchild": 0,
"show_in_menus": true,
"live": false,
"depth": 4,
@ -118,7 +118,7 @@
"model": "wagtailcore.page",
"fields": {
"title": "Someone Else's Event",
"numchild": 1,
"numchild": 0,
"show_in_menus": true,
"live": false,
"depth": 4,

View file

@ -7,6 +7,7 @@ from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, Inli
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtaildocs.edit_handlers import DocumentChooserPanel
from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
from wagtail.wagtailsnippets.models import register_snippet
EVENT_AUDIENCE_CHOICES = (
@ -252,3 +253,20 @@ FormPage.content_panels = [
FieldPanel('subject', classname="full"),
], "Email")
]
# Snippets
class Advert(models.Model):
url = models.URLField(null=True, blank=True)
text = models.CharField(max_length=255)
panels = [
FieldPanel('url'),
FieldPanel('text'),
]
def __unicode__(self):
return self.text
register_snippet(Advert)

View file

@ -0,0 +1,11 @@
{% load pageurl %}
<!DOCTYPE HTML>
<html>
<head>
<title>{{ self.title }}</title>
</head>
<body>
<h1>{{ self.title }}</h1>
<h2>Simple page</h2>
</body>
</html>

View file

@ -14,7 +14,9 @@ except ImportError:
def login(client):
# Create a user
User.objects.create_superuser(username='test', email='test@email.com', password='password')
user = User.objects.create_superuser(username='test', email='test@email.com', password='password')
# Login
client.login(username='test', password='password')
return user

View file

View file

@ -0,0 +1,320 @@
from django.test import TestCase
from wagtail.tests.utils import login, unittest
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core import mail
class TestAuthentication(TestCase):
"""
This tests that users can login and logout of the admin interface
"""
def setUp(self):
login(self.client)
def test_login_view(self):
"""
This tests that the login view responds with a login page
"""
# Logout so we can test the login view
self.client.logout()
# Get login page
response = self.client.get(reverse('wagtailadmin_login'))
# Check that the user recieved a login page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/login.html')
def test_login_view_post(self):
"""
This posts user credentials to the login view and checks that
the user was logged in successfully
"""
# Logout so we can test the login view
self.client.logout()
# Post credentials to the login page
post_data = {
'username': 'test',
'password': 'password',
}
response = self.client.post(reverse('wagtailadmin_login'), post_data)
# Check that the user was redirected to the dashboard
self.assertEqual(response.status_code, 302)
# Check that the user was logged in
self.assertTrue('_auth_user_id' in self.client.session)
self.assertEqual(self.client.session['_auth_user_id'], User.objects.get(username='test').id)
@unittest.expectedFailure # See: https://github.com/torchbox/wagtail/issues/25
def test_already_logged_in_redirect(self):
"""
This tests that a user who is already logged in is automatically
redirected to the admin dashboard if they try to access the login
page
"""
# Get login page
response = self.client.get(reverse('wagtailadmin_login'))
# Check that the user was redirected to the dashboard
self.assertEqual(response.status_code, 302)
def test_logout(self):
"""
This tests that the user can logout
"""
# Get logout page page
response = self.client.get(reverse('wagtailadmin_logout'))
# Check that the user was redirected to the login page
self.assertEqual(response.status_code, 302)
# Check that the user was logged out
self.assertFalse('_auth_user_id' in self.client.session)
class TestAccountSection(TestCase):
"""
This tests that the accounts section is working
"""
def setUp(self):
login(self.client)
def test_account_view(self):
"""
This tests that the login view responds with a login page
"""
# Get account page
response = self.client.get(reverse('wagtailadmin_account'))
# Check that the user recieved an account page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/account/account.html')
def test_change_password_view(self):
"""
This tests that the change password view responds with a change password page
"""
# Get change password page
response = self.client.get(reverse('wagtailadmin_account_change_password'))
# Check that the user recieved a change password page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/account/change_password.html')
def test_change_password_view_post(self):
"""
This posts a new password to the change password view and checks
that the users password was changed
"""
# Post new password to change password page
post_data = {
'new_password1': 'newpassword',
'new_password2': 'newpassword',
}
response = self.client.post(reverse('wagtailadmin_account_change_password'), post_data)
# Check that the user was redirected
self.assertEqual(response.status_code, 302)
# Check that the password was changed
self.assertTrue(User.objects.get(username='test').check_password('newpassword'))
def test_change_password_view_post_password_mismatch(self):
"""
This posts a two passwords that don't match to the password change
view and checks that a validation error was raised
"""
# Post new password to change password page
post_data = {
'new_password1': 'newpassword',
'new_password2': 'badpassword',
}
response = self.client.post(reverse('wagtailadmin_account_change_password'), post_data)
# Check that the user wasn't redirected
self.assertEqual(response.status_code, 200)
# Check that a validation error was raised
self.assertTrue('new_password2' in response.context['form'].errors.keys())
self.assertTrue("The two password fields didn't match." in response.context['form'].errors['new_password2'])
# Check that the password was not changed
self.assertTrue(User.objects.get(username='test').check_password('password'))
class TestPasswordReset(TestCase):
"""
This tests that the password reset is working
"""
def setUp(self):
# Create a user
User.objects.create_superuser(username='test', email='test@email.com', password='password')
def test_password_reset_view(self):
"""
This tests that the password reset view returns a password reset page
"""
# Get password reset page
response = self.client.get(reverse('password_reset'))
# Check that the user recieved a password reset page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/form.html')
def test_password_reset_view_post(self):
"""
This posts an email address to the password reset view and
checks that a password reset email was sent
"""
# Post email address to password reset view
post_data = {
'email': 'test@email.com',
}
response = self.client.post(reverse('password_reset'), post_data)
# Check that the user was redirected
self.assertEqual(response.status_code, 302)
# Check that a password reset email was sent to the user
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ['test@email.com'])
self.assertEqual(mail.outbox[0].subject, "Password reset")
def test_password_reset_view_post_unknown_email(self):
"""
This posts an unknown email address to the password reset view and
checks that the password reset form raises a validation error
"""
post_data = {
'email': 'unknown@email.com',
}
response = self.client.post(reverse('password_reset'), post_data)
# Check that the user wasn't redirected
self.assertEqual(response.status_code, 200)
# Check that a validation error was raised
self.assertTrue('__all__' in response.context['form'].errors.keys())
self.assertTrue("This email address is not recognised." in response.context['form'].errors['__all__'])
# Check that an email was not sent
self.assertEqual(len(mail.outbox), 0)
def test_password_reset_view_post_invalid_email(self):
"""
This posts an incalid email address to the password reset view and
checks that the password reset form raises a validation error
"""
post_data = {
'email': 'Hello world!',
}
response = self.client.post(reverse('password_reset'), post_data)
# Check that the user wasn't redirected
self.assertEqual(response.status_code, 200)
# Check that a validation error was raised
self.assertTrue('email' in response.context['form'].errors.keys())
self.assertTrue("Enter a valid email address." in response.context['form'].errors['email'])
# Check that an email was not sent
self.assertEqual(len(mail.outbox), 0)
def setup_password_reset_confirm_tests(self):
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
# Get user
self.user = User.objects.get(username='test')
# Generate a password reset token
self.password_reset_token = PasswordResetTokenGenerator().make_token(self.user)
# Generate a password reset uid
self.password_reset_uid = urlsafe_base64_encode(force_bytes(self.user.pk))
# Create url_args
self.url_kwargs = dict(uidb64=self.password_reset_uid, token=self.password_reset_token)
def test_password_reset_confirm_view(self):
"""
This tests that the password reset confirm view returns a password reset confirm page
"""
self.setup_password_reset_confirm_tests()
# Get password reset confirm page
response = self.client.get(reverse('password_reset_confirm', kwargs=self.url_kwargs))
# Check that the user recieved a password confirm done page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/confirm.html')
def test_password_reset_confirm_view_post(self):
"""
This posts a new password to the password reset confirm view and checks
that the users password was changed
"""
self.setup_password_reset_confirm_tests()
# Post new password to change password page
post_data = {
'new_password1': 'newpassword',
'new_password2': 'newpassword',
}
response = self.client.post(reverse('password_reset_confirm', kwargs=self.url_kwargs), post_data)
# Check that the user was redirected
self.assertEqual(response.status_code, 302)
# Check that the password was changed
self.assertTrue(User.objects.get(username='test').check_password('newpassword'))
def test_password_reset_confirm_view_post_password_mismatch(self):
"""
This posts a two passwords that don't match to the password reset
confirm view and checks that a validation error was raised
"""
self.setup_password_reset_confirm_tests()
# Post new password to change password page
post_data = {
'new_password1': 'newpassword',
'new_password2': 'badpassword',
}
response = self.client.post(reverse('password_reset_confirm', kwargs=self.url_kwargs), post_data)
# Check that the user wasn't redirected
self.assertEqual(response.status_code, 200)
# Check that a validation error was raised
self.assertTrue('new_password2' in response.context['form'].errors.keys())
self.assertTrue("The two password fields didn't match." in response.context['form'].errors['new_password2'])
# Check that the password was not changed
self.assertTrue(User.objects.get(username='test').check_password('password'))
def test_password_reset_done_view(self):
"""
This tests that the password reset done view returns a password reset done page
"""
# Get password reset done page
response = self.client.get(reverse('password_reset_done'))
# Check that the user recieved a password reset done page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/done.html')
def test_password_reset_complete_view(self):
"""
This tests that the password reset complete view returns a password reset complete page
"""
# Get password reset complete page
response = self.client.get(reverse('password_reset_complete'))
# Check that the user recieved a password reset complete page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/complete.html')

View file

@ -1,22 +1,12 @@
from datetime import timedelta
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.test import TestCase
from wagtail.tests.models import SimplePage, EventPage
from wagtail.tests.utils import login, unittest
from wagtail.wagtailcore.models import Page, PageRevision
class TestHome(TestCase):
def setUp(self):
# Login
login(self.client)
def test_status_code(self):
response = self.client.get(reverse('wagtailadmin_home'))
self.assertEqual(response.status_code, 200)
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User, Permission
from django.core import mail
from django.utils import timezone
class TestPageExplorer(TestCase):
@ -46,12 +36,26 @@ class TestPageCreation(TestCase):
self.root_page = Page.objects.get(id=2)
# Login
login(self.client)
self.user = login(self.client)
def test_add_subpage(self):
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.root_page.id, )))
self.assertEqual(response.status_code, 200)
def test_add_subpage_bad_permissions(self):
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Get add subpage page
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.root_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_add_subpage_nonexistantparent(self):
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(100000, )))
self.assertEqual(response.status_code, 404)
@ -60,6 +64,20 @@ class TestPageCreation(TestCase):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)))
self.assertEqual(response.status_code, 200)
def test_create_simplepage_bad_permissions(self):
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Get page
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_create_simplepage_post(self):
post_data = {
'title': "New page!",
@ -175,6 +193,36 @@ class TestPageCreation(TestCase):
self.assertFalse(page.live)
self.assertTrue(page.status_string, "scheduled")
def test_create_simplepage_post_submit(self):
# Create a moderator user for testing email
moderator = User.objects.create_superuser('moderator', 'moderator@email.com', 'password')
# Submit
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
self.assertEqual(page.title, post_data['title'])
self.assertIsInstance(page, SimplePage)
self.assertFalse(page.live)
# The latest revision for the page should now be in moderation
self.assertTrue(page.get_latest_revision().submitted_for_moderation)
# Check that the moderator got an email
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ['moderator@email.com'])
self.assertEqual(mail.outbox[0].subject, 'The page "New page!" has been submitted for moderation')
def test_create_simplepage_post_existingslug(self):
# This tests the existing slug checking on page save
@ -200,11 +248,25 @@ class TestPageCreation(TestCase):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', 100000)))
self.assertEqual(response.status_code, 404)
@unittest.expectedFailure # FIXME: Crashes!
@unittest.expectedFailure # FIXME: Crashes!
def test_create_nonpagetype(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('wagtailimages', 'image', self.root_page.id)))
self.assertEqual(response.status_code, 404)
def test_preview_on_create(self):
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_preview_on_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Check the response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'tests/simple_page.html')
self.assertContains(response, "New page!")
class TestPageEdit(TestCase):
def setUp(self):
@ -226,29 +288,13 @@ class TestPageEdit(TestCase):
self.root_page.add_child(instance=self.event_page)
# Login
login(self.client)
self.user = login(self.client)
def test_edit_page(self):
def test_page_edit(self):
# Tests that the edit page loads
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.event_page.id, )))
self.assertEqual(response.status_code, 200)
def test_edit_post(self):
# Tests simple editing
post_data = {
'title': "I've been edited!",
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# The page should have "has_unpublished_changes" flag set
child_page_new = SimplePage.objects.get(id=self.child_page.id)
self.assertTrue(child_page_new.has_unpublished_changes)
def test_edit_post_scheduled(self):
go_live_at = timezone.now() + timedelta(days=1)
expire_at = timezone.now() + timedelta(days=2)
@ -276,7 +322,37 @@ class TestPageEdit(TestCase):
self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(go_live_at.date())).exists())
self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(expire_at.date())).exists())
def test_edit_post_publish(self):
def test_page_edit_bad_permissions(self):
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Get edit page
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_page_edit_post(self):
# Tests simple editing
post_data = {
'title': "I've been edited!",
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# The page should have "has_unpublished_changes" flag set
child_page_new = SimplePage.objects.get(id=self.child_page.id)
self.assertTrue(child_page_new.has_unpublished_changes)
def test_page_edit_post_publish(self):
# Tests publish from edit page
post_data = {
'title': "I've been edited!",
@ -285,7 +361,7 @@ class TestPageEdit(TestCase):
'action-publish': "Publish",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
@ -367,6 +443,48 @@ class TestPageEdit(TestCase):
# And a revision with approved_go_live_at should not exist
self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_at__isnull=True).exists())
def test_page_edit_post_submit(self):
# Create a moderator user for testing email
moderator = User.objects.create_superuser('moderator', 'moderator@email.com', 'password')
# Tests submitting from edit page
post_data = {
'title': "I've been edited!",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# The page should have "has_unpublished_changes" flag set
child_page_new = SimplePage.objects.get(id=self.child_page.id)
self.assertTrue(child_page_new.has_unpublished_changes)
# The latest revision for the page should now be in moderation
self.assertTrue(child_page_new.get_latest_revision().submitted_for_moderation)
# Check that the moderator got an email
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ['moderator@email.com'])
self.assertEqual(mail.outbox[0].subject, 'The page "Hello world!" has been submitted for moderation') # Note: should this be "I've been edited!"?
def test_preview_on_edit(self):
post_data = {
'title': "I've been edited!",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_preview_on_edit', args=(self.child_page.id, )), post_data)
# Check the response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'tests/simple_page.html')
self.assertContains(response, "I&#39;ve been edited!")
class TestPageDelete(TestCase):
def setUp(self):
@ -380,14 +498,28 @@ class TestPageDelete(TestCase):
self.root_page.add_child(instance=self.child_page)
# Login
login(self.client)
self.user = login(self.client)
def test_delete(self):
def test_page_delete(self):
response = self.client.get(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
def test_delete_post(self):
post_data = {'hello': 'world'} # For some reason, this test doesn't work without a bit of POST data
def test_page_delete_bad_permissions(self):
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Get delete page
response = self.client.get(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_page_delete_post(self):
post_data = {'hello': 'world'} # For some reason, this test doesn't work without a bit of POST data
response = self.client.post(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer page
@ -402,25 +534,36 @@ class TestPageSearch(TestCase):
# Login
login(self.client)
def get(self, params={}):
return self.client.get(reverse('wagtailadmin_pages_search'), params)
def get(self, params=None, **extra):
return self.client.get(reverse('wagtailadmin_pages_search'), params or {}, **extra)
def test_status_code(self):
self.assertEqual(self.get().status_code, 200)
def test_view(self):
response = self.get()
self.assertTemplateUsed(response, 'wagtailadmin/pages/search.html')
self.assertEqual(response.status_code, 200)
def test_search(self):
response = self.get({'q': "Hello"})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/search.html')
self.assertEqual(response.context['query_string'], "Hello")
def test_ajax(self):
response = self.get({'q': "Hello"}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
self.assertTemplateNotUsed(response, 'wagtailadmin/pages/search.html')
self.assertTemplateUsed(response, 'wagtailadmin/pages/search_results.html')
self.assertEqual(response.context['query_string'], "Hello")
def test_pagination(self):
pages = ['0', '1', '-1', '9999', 'Not a page']
for page in pages:
response = self.get({'p': page})
response = self.get({'q': "Hello", 'p': page})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/search.html')
def test_root_can_appear_in_search_results(self):
response = self.client.get('/admin/pages/search/?q=roo')
response = self.get({'q': "roo"})
self.assertEqual(response.status_code, 200)
# 'pages' list in the response should contain root
results = response.context['pages']
@ -450,12 +593,26 @@ class TestPageMove(TestCase):
self.section_a.add_child(instance=self.test_page)
# Login
login(self.client)
self.user = login(self.client)
def test_page_move(self):
response = self.client.get(reverse('wagtailadmin_pages_move', args=(self.test_page.id, )))
self.assertEqual(response.status_code, 200)
def test_page_move_bad_permissions(self):
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Get move page
response = self.client.get(reverse('wagtailadmin_pages_move', args=(self.test_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_page_move_confirm(self):
response = self.client.get(reverse('wagtailadmin_pages_move_confirm', args=(self.test_page.id, self.section_b.id)))
self.assertEqual(response.status_code, 200)
@ -465,19 +622,220 @@ class TestPageMove(TestCase):
self.assertEqual(response.status_code, 200)
class TestEditorHooks(TestCase):
class TestPageUnpublish(TestCase):
def setUp(self):
self.homepage = Page.objects.get(id=2)
login(self.client)
self.user = login(self.client)
def test_editor_css_and_js_hooks_on_add(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.homepage.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')
# Create a page to unpublish
root_page = Page.objects.get(id=2)
self.page = SimplePage(
title="Hello world!",
slug='hello-world',
live=True,
)
root_page.add_child(instance=self.page)
def test_editor_css_and_js_hooks_on_edit(self):
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.homepage.id, )))
def test_unpublish_view(self):
"""
This tests that the unpublish view responds with an unpublish confirm page
"""
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )))
# Check that the user recieved an unpublish confirm page
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')
self.assertTemplateUsed(response, 'wagtailadmin/pages/confirm_unpublish.html')
def test_unpublish_view_invalid_page_id(self):
"""
This tests that the unpublish view returns an error if the page id is invalid
"""
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages_unpublish', args=(12345, )))
# Check that the user recieved a 404 response
self.assertEqual(response.status_code, 404)
def test_unpublish_view_bad_permissions(self):
"""
This tests that the unpublish view doesn't allow users without unpublish permissions
"""
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_unpublish_view_post(self):
"""
This posts to the unpublish view and checks that the page was unpublished
"""
# Post to the unpublish page
response = self.client.post(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )), {
'foo': "Must post something or the view won't see this as a POST request",
})
# Check that the user was redirected
self.assertEqual(response.status_code, 302)
# Check that the page was unpublished
self.assertFalse(SimplePage.objects.get(id=self.page.id).live)
class TestApproveRejectModeration(TestCase):
def setUp(self):
self.submitter = User.objects.create_superuser(
username='submitter',
email='submitter@email.com',
password='password',
)
self.user = login(self.client)
# Create a page and submit it for moderation
root_page = Page.objects.get(id=2)
self.page = SimplePage(
title="Hello world!",
slug='hello-world',
live=False,
)
root_page.add_child(instance=self.page)
self.page.save_revision(user=self.submitter, submitted_for_moderation=True)
self.revision = self.page.get_latest_revision()
def test_approve_moderation_view(self):
"""
This posts to the approve moderation view and checks that the page was approved
"""
# Post
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )), {
'foo': "Must post something or the view won't see this as a POST request",
})
# Check that the user was redirected
self.assertEqual(response.status_code, 302)
# Page must be live
self.assertTrue(Page.objects.get(id=self.page.id).live)
# Submitter must recieve an approved email
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ['submitter@email.com'])
self.assertEqual(mail.outbox[0].subject, 'The page "Hello world!" has been approved')
def test_approve_moderation_view_bad_revision_id(self):
"""
This tests that the approve moderation view handles invalid revision ids correctly
"""
# Post
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(12345, )), {
'foo': "Must post something or the view won't see this as a POST request",
})
# Check that the user recieved a 404 response
self.assertEqual(response.status_code, 404)
def test_approve_moderation_view_bad_permissions(self):
"""
This tests that the approve moderation view doesn't allow users without moderation permissions
"""
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Post
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )), {
'foo': "Must post something or the view won't see this as a POST request",
})
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_reject_moderation_view(self):
"""
This posts to the reject moderation view and checks that the page was rejected
"""
# Post
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )), {
'foo': "Must post something or the view won't see this as a POST request",
})
# Check that the user was redirected
self.assertEqual(response.status_code, 302)
# Page must not be live
self.assertFalse(Page.objects.get(id=self.page.id).live)
# Revision must no longer be submitted for moderation
self.assertFalse(PageRevision.objects.get(id=self.revision.id).submitted_for_moderation)
# Submitter must recieve a rejected email
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ['submitter@email.com'])
self.assertEqual(mail.outbox[0].subject, 'The page "Hello world!" has been rejected')
def test_reject_moderation_view_bad_revision_id(self):
"""
This tests that the reject moderation view handles invalid revision ids correctly
"""
# Post
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(12345, )), {
'foo': "Must post something or the view won't see this as a POST request",
})
# Check that the user recieved a 404 response
self.assertEqual(response.status_code, 404)
def test_reject_moderation_view_bad_permissions(self):
"""
This tests that the reject moderation view doesn't allow users without moderation permissions
"""
# Remove privileges from user
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
# Post
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )), {
'foo': "Must post something or the view won't see this as a POST request",
})
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_preview_for_moderation(self):
response = self.client.get(reverse('wagtailadmin_pages_preview_for_moderation', args=(self.revision.id, )))
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'tests/simple_page.html')
self.assertContains(response, "Hello world!")
class TestContentTypeUse(TestCase):
fixtures = ['test.json']
def setUp(self):
self.user = login(self.client)
def test_content_type_use(self):
# Get use of event page
response = self.client.get(reverse('wagtailadmin_pages_type_use', args=('tests', 'eventpage')))
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/content_type_use.html')
self.assertContains(response, "Christmas")

View file

@ -0,0 +1,46 @@
from django.test import TestCase
from wagtail.tests.models import SimplePage, EventPage
from wagtail.tests.utils import login, unittest
from wagtail.wagtailcore.models import Page
from wagtail.wagtailadmin.tasks import send_email_task
from django.core.urlresolvers import reverse
from django.core import mail
class TestHome(TestCase):
def setUp(self):
# Login
login(self.client)
def test_status_code(self):
response = self.client.get(reverse('wagtailadmin_home'))
self.assertEqual(response.status_code, 200)
class TestEditorHooks(TestCase):
def setUp(self):
self.homepage = Page.objects.get(id=2)
login(self.client)
def test_editor_css_and_js_hooks_on_add(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.homepage.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')
def test_editor_css_and_js_hooks_on_edit(self):
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.homepage.id, )))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')
class TestSendEmailTask(TestCase):
def test_send_email(self):
send_email_task("Test subject", "Test content", ["nobody@email.com"], "test@email.com")
# Check that the email was sent
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "Test subject")
self.assertEqual(mail.outbox[0].body, "Test content")
self.assertEqual(mail.outbox[0].to, ["nobody@email.com"])

View file

@ -12,15 +12,15 @@ class Command(NoArgsCommand):
try:
page.specific
except ObjectDoesNotExist:
print "Page %d (%s) is missing a subclass record; deleting." % (page.id, page.title)
self.stdout.write("Page %d (%s) is missing a subclass record; deleting." % (page.id, page.title))
problems_found = True
page.delete()
(_, _, _, bad_depth, bad_numchild) = Page.find_problems()
if bad_depth:
print "Incorrect depth value found for pages: %r" % bad_depth
self.stdout.write("Incorrect depth value found for pages: %r" % bad_depth)
if bad_numchild:
print "Incorrect numchild value found for pages: %r" % bad_numchild
self.stdout.write("Incorrect numchild value found for pages: %r" % bad_numchild)
if bad_depth or bad_numchild:
Page.fix_tree(destructive=False)
@ -28,20 +28,20 @@ class Command(NoArgsCommand):
remaining_problems = Page.find_problems()
if any(remaining_problems):
print "Remaining problems (cannot fix automatically):"
self.stdout.write("Remaining problems (cannot fix automatically):")
(bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = remaining_problems
if bad_alpha:
print "Invalid characters found in path for pages: %r" % bad_alpha
self.stdout.write("Invalid characters found in path for pages: %r" % bad_alpha)
if bad_path:
print "Invalid path length found for pages: %r" % bad_path
self.stdout.write("Invalid path length found for pages: %r" % bad_path)
if orphans:
print "Orphaned pages found: %r" % orphans
self.stdout.write("Orphaned pages found: %r" % orphans)
if bad_depth:
print "Incorrect depth value found for pages: %r" % bad_depth
self.stdout.write("Incorrect depth value found for pages: %r" % bad_depth)
if bad_numchild:
print "Incorrect numchild value found for pages: %r" % bad_numchild
self.stdout.write("Incorrect numchild value found for pages: %r" % bad_numchild)
elif problems_found:
print "All problems fixed."
self.stdout.write("All problems fixed.")
else:
print "No problems found."
self.stdout.write("No problems found.")

View file

@ -15,8 +15,8 @@ class Command(BaseCommand):
pages = from_page.get_children()
# Move the pages
print 'Moving ' + str(len(pages)) + ' pages from "' + from_page.title + '" to "' + to_page.title + '"'
self.stdout.write('Moving ' + str(len(pages)) + ' pages from "' + from_page.title + '" to "' + to_page.title + '"')
for page in pages:
page.move(to_page, pos='last-child')
print 'Done'
self.stdout.write('Done')

View file

@ -24,7 +24,7 @@ class Command(BaseCommand):
revision.save(update_fields=['content_json'])
for content_type in get_page_types():
print "scanning %s" % content_type.name
self.stdout.write("scanning %s" % content_type.name)
page_class = content_type.model_class()
try:

View file

@ -1,4 +1,5 @@
from datetime import timedelta
from StringIO import StringIO
from django.test import TestCase, Client
from django.http import HttpRequest, Http404
@ -904,3 +905,83 @@ class TestPublishScheduledPagesCommand(TestCase):
p = Page.objects.get(slug='hello-world')
self.assertFalse(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists())
class TestFixTreeCommand(TestCase):
fixtures = ['test.json']
def run_command(self):
management.call_command('fixtree', interactive=False, stdout=StringIO())
def test_fixes_numchild(self):
# Get homepage and save old value
homepage = Page.objects.get(url_path='/home/')
old_numchild = homepage.numchild
# Break it
homepage.numchild = 12345
homepage.save()
# Check that its broken
self.assertEqual(Page.objects.get(url_path='/home/').numchild, 12345)
# Call command
self.run_command()
# Check if its fixed
self.assertEqual(Page.objects.get(url_path='/home/').numchild, old_numchild)
def test_fixes_depth(self):
# Get homepage and save old value
homepage = Page.objects.get(url_path='/home/')
old_depth = homepage.depth
# Break it
homepage.depth = 12345
homepage.save()
# Check that its broken
self.assertEqual(Page.objects.get(url_path='/home/').depth, 12345)
# Call command
self.run_command()
# Check if its fixed
self.assertEqual(Page.objects.get(url_path='/home/').depth, old_depth)
class TestMovePagesCommand(TestCase):
fixtures = ['test.json']
def run_command(self, from_, to):
management.call_command('move_pages', str(from_), str(to), interactive=False, stdout=StringIO())
def test_move_pages(self):
# Get pages
events_index = Page.objects.get(url_path='/home/events/')
about_us = Page.objects.get(url_path='/home/about-us/')
page_ids = events_index.get_children().values_list('id', flat=True)
# Move all events into "about us"
self.run_command(events_index.id, about_us.id)
# Check that all pages moved
for page_id in page_ids:
self.assertEqual(Page.objects.get(id=page_id).get_parent(), about_us)
class TestReplaceTextCommand(TestCase):
fixtures = ['test.json']
def run_command(self, from_text, to_text):
management.call_command('replace_text', from_text, to_text, interactive=False, stdout=StringIO())
def test_replace_text(self):
# Check that the christmas page is definitely about christmas
self.assertEqual(Page.objects.get(url_path='/home/events/christmas/').title, "Christmas")
# Make it about easter
self.run_command("Christmas", "Easter")
# Check that its now about easter
self.assertEqual(Page.objects.get(url_path='/home/events/christmas/').title, "Easter")

View file

@ -1,19 +1,139 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from wagtail.tests.utils import unittest
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from wagtail.tests.utils import login, unittest
from wagtail.tests.models import Advert
@unittest.skip("Need real tests")
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
class TestSnippetIndexView(TestCase):
def setUp(self):
login(self.client)
def get(self, params={}):
return self.client.get(reverse('wagtailsnippets_index'), params)
def test_status_code(self):
self.assertEqual(self.get().status_code, 200)
def test_displays_snippet(self):
self.assertContains(self.get(), "Adverts")
class TestSnippetListView(TestCase):
def setUp(self):
login(self.client)
def get(self, params={}):
return self.client.get(reverse('wagtailsnippets_list',
args=('tests', 'advert')),
params)
def test_status_code(self):
self.assertEqual(self.get().status_code, 200)
def test_displays_add_button(self):
self.assertContains(self.get(), "Add advert")
class TestSnippetCreateView(TestCase):
def setUp(self):
login(self.client)
def get(self, params={}):
return self.client.get(reverse('wagtailsnippets_create',
args=('tests', 'advert')),
params)
def post(self, post_data={}):
return self.client.post(reverse('wagtailsnippets_create',
args=('tests', 'advert')),
post_data)
def test_status_code(self):
self.assertEqual(self.get().status_code, 200)
def test_create_invalid(self):
response = self.post(post_data={'foo': 'bar'})
self.assertContains(response, "The snippet could not be created due to errors.")
self.assertContains(response, "This field is required.")
def test_create(self):
response = self.post(post_data={'text': 'test_advert',
'url': 'http://www.example.com/'})
self.assertEqual(response.status_code, 302)
snippets = Advert.objects.filter(text='test_advert')
self.assertEqual(snippets.count(), 1)
self.assertEqual(snippets.first().url, 'http://www.example.com/')
class TestSnippetEditView(TestCase):
def setUp(self):
self.test_snippet = Advert()
self.test_snippet.text = 'test_advert'
self.test_snippet.url = 'http://www.example.com/'
self.test_snippet.save()
login(self.client)
def get(self, params={}):
return self.client.get(reverse('wagtailsnippets_edit',
args=('tests', 'advert', self.test_snippet.id)),
params)
def post(self, post_data={}):
return self.client.post(reverse('wagtailsnippets_edit',
args=('tests', 'advert', self.test_snippet.id)),
post_data)
def test_status_code(self):
self.assertEqual(self.get().status_code, 200)
def test_non_existant_model(self):
response = self.client.get(reverse('wagtailsnippets_edit',
args=('tests', 'foo', self.test_snippet.id)))
self.assertEqual(response.status_code, 404)
def test_nonexistant_id(self):
response = self.client.get(reverse('wagtailsnippets_edit',
args=('tests', 'advert', 999999)))
self.assertEqual(response.status_code, 404)
def test_edit_invalid(self):
response = self.post(post_data={'foo': 'bar'})
self.assertContains(response, "The snippet could not be saved due to errors.")
self.assertContains(response, "This field is required.")
def test_edit(self):
response = self.post(post_data={'text': 'edited_test_advert',
'url': 'http://www.example.com/edited'})
self.assertEqual(response.status_code, 302)
snippets = Advert.objects.filter(text='edited_test_advert')
self.assertEqual(snippets.count(), 1)
self.assertEqual(snippets.first().url, 'http://www.example.com/edited')
class TestSnippetDelete(TestCase):
def setUp(self):
self.test_snippet = Advert()
self.test_snippet.text = 'test_advert'
self.test_snippet.url = 'http://www.example.com/'
self.test_snippet.save()
login(self.client)
def test_delete_get(self):
response = self.client.get(reverse('wagtailsnippets_delete', args=('tests', 'advert', self.test_snippet.id, )))
self.assertEqual(response.status_code, 200)
def test_delete_post(self):
post_data = {'foo': 'bar'} # For some reason, this test doesn't work without a bit of POST data
response = self.client.post(reverse('wagtailsnippets_delete', args=('tests', 'advert', self.test_snippet.id, )), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the page is gone
self.assertEqual(Advert.objects.filter(text='test_advert').count(), 0)

View file

@ -125,7 +125,7 @@ def create(request, content_type_app_name, content_type_model_name):
messages.success(
request,
_("{snippet_type} '{instance}' created.").format(
snippet_type=capfirst(get_snippet_type_name(content_type)[0]),
snippet_type=capfirst(get_snippet_type_name(content_type)[0]),
instance=instance
)
)
@ -166,7 +166,7 @@ def edit(request, content_type_app_name, content_type_model_name, id):
messages.success(
request,
_("{snippet_type} '{instance}' updated.").format(
snippet_type=capfirst(snippet_type_name),
snippet_type=capfirst(snippet_type_name),
instance=instance
)
)
@ -202,7 +202,7 @@ def delete(request, content_type_app_name, content_type_model_name, id):
messages.success(
request,
_("{snippet_type} '{instance}' deleted.").format(
snippet_type=capfirst(snippet_type_name),
snippet_type=capfirst(snippet_type_name),
instance=instance
)
)