From 6947eab55b77cfd104daf26cfab30313b22f397f Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Tue, 6 Oct 2015 11:25:16 +1100 Subject: [PATCH] Add test helpers for Wagtail sites `wagtail.tests.utils.WagtailPageTests` is a new `TestCase` subclass that helps developers write tests for their Wagtail sites. It currently includes three assert methods: `assertCanCreateAt(parent_model, child_model)`, which asserts that a child page of a certain type could be created underneath a parent page. This is useful for making assertions around the business rules of your site. `assertCanNotCreateAt(parent_model, child_model)` is the inverse of the above. `assertCanCreate(parent, child_model, data)` asserts that a child page can be added underneath the given parent page, by POSTing `data` at the correct page in the Wagtail admin. This checks that the developer has correctly configured their `content_panels` and related options. These methods are just a start, and could be expanded further. More methods could be added, asserting that Snippets can be created, for example. The current methods could be extended further, to validate more about the Wagtail admin page editor. --- wagtail/tests/utils.py | 100 +++++++++++++++++++++++- wagtail/wagtailcore/tests/test_tests.py | 57 ++++++++++++++ 2 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 wagtail/wagtailcore/tests/test_tests.py diff --git a/wagtail/tests/utils.py b/wagtail/tests/utils.py index 3cfca1f098..939ba0496a 100644 --- a/wagtail/tests/utils.py +++ b/wagtail/tests/utils.py @@ -1,9 +1,18 @@ -from contextlib import contextmanager -import warnings -import sys +from __future__ import absolute_import, unicode_literals +import sys +import warnings +from contextlib import contextmanager + +import django from django.contrib.auth import get_user_model +from django.contrib.contenttypes.models import ContentType +from django.core.urlresolvers import reverse +from django.test import TestCase from django.utils import six +from django.utils.text import slugify + +from wagtail.wagtailcore.models import Page class WagtailTestUtils(object): @@ -51,3 +60,88 @@ class WagtailTestUtils(object): for mod in list(sys.modules.values()): if hasattr(mod, key): getattr(mod, key).clear() + + +class WagtailPageTests(WagtailTestUtils, TestCase): + """ + A set of asserts to help write tests for your own Wagtail site. + """ + def setUp(self): + super(WagtailPageTests, self).setUp() + self.login() + + def _testCanCreateAt(self, parent_model, child_model): + child_ct = ContentType.objects.get_for_model(child_model) + parent_ct = ContentType.objects.get_for_model(parent_model) + + return all([ + child_ct in parent_model.allowed_subpage_types(), + # Anything can be created under a Page + parent_model is Page or parent_ct in child_model.allowed_parent_page_types()]) + + def assertCanCreateAt(self, parent_model, child_model, msg=None): + """ + Assert a particular child Page type can be created under a parent + Page type. + """ + if not self._testCanCreateAt(parent_model, child_model): + msg = self._formatMessage(msg, "Can not create a %s.%s under a %s.%s" % ( + child_model._meta.app_label, child_model._meta.model_name, + parent_model._meta.app_label, parent_model._meta.model_name)) + raise self.failureException(msg) + + def assertCanNotCreateAt(self, parent_model, child_model, msg=None): + """ + Assert a particular child Page type can not be created under a parent + Page type. + """ + if self._testCanCreateAt(parent_model, child_model): + msg = self._formatMessage(msg, "Can create a %s.%s under a %s.%s" % ( + child_model._meta.app_label, child_model._meta.model_name, + parent_model._meta.app_label, parent_model._meta.model_name)) + raise self.failureException(msg) + + def assertCanCreate(self, parent, child_model, data, msg=None): + """ + Assert that a child of the given Page type can be created under the + parent, using the supplied POST data + """ + self.assertCanCreateAt(parent.specific_class, child_model) + + if 'slug' not in data and 'title' in data: + data['slug'] = slugify(data['title']) + data['action-publish'] = 'action-publish' + + add_url = reverse('wagtailadmin_pages:add', args=[ + child_model._meta.app_label, child_model._meta.model_name, parent.pk]) + response = self.client.post(add_url, data, follow=True) + + if response.status_code != 200: + msg = self._formatMessage(msg, 'Creating a %s.%s returned a %d' % ( + child_model._meta.app_label, child_model._meta.model_name, response.status_code)) + raise self.failureException(msg) + + if response.redirect_chain == []: + if 'form' not in response.context: + msg = self._formatMessage(msg, 'Creating a page failed unusually') + raise self.failureException(msg) + form = response.context['form'] + if not form.errors: + msg = self._formatMessage(msg, 'Creating a page failed for an unknown reason') + raise self.failureException(msg) + + errors = '\n'.join(' %s:\n %s' % (field, '\n '.join(errors)) + for field, errors in sorted(form.errors.items())) + msg = self._formatMessage(msg, 'Validation errors found when creating a %s.%s:\n%s' % ( + child_model._meta.app_label, child_model._meta.model_name, errors)) + raise self.failureException(msg) + + if django.VERSION >= (1, 9): + explore_url = reverse('wagtailadmin_explore', args=[parent.pk]) + else: + explore_url = 'http://testserver' + reverse('wagtailadmin_explore', args=[parent.pk]) + if response.redirect_chain != [(explore_url, 302)]: + msg = self._formatMessage(msg, 'Creating a page %s.%s didnt redirect the user to the explorer, but to %s' % ( + child_model._meta.app_label, child_model._meta.model_name, + response.redirect_chain)) + raise self.failureException(msg) diff --git a/wagtail/wagtailcore/tests/test_tests.py b/wagtail/wagtailcore/tests/test_tests.py new file mode 100644 index 0000000000..41ddeb8c8a --- /dev/null +++ b/wagtail/wagtailcore/tests/test_tests.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import, unicode_literals + +import json + +from wagtail.tests.testapp.models import ( + BusinessChild, EventIndex, EventPage, SimplePage, StreamPage) +from wagtail.tests.utils import WagtailPageTests +from wagtail.wagtailcore.models import Page, Site + + +class TestWagtailPageTests(WagtailPageTests): + def setUp(self): + super(TestWagtailPageTests, self).setUp() + site = Site.objects.get(is_default_site=True) + self.root = site.root_page.specific + + def test_assert_can_create_at(self): + # It should be possible to create an EventPage under an EventIndex, + self.assertCanCreateAt(EventIndex, EventPage) + self.assertCanCreateAt(Page, EventIndex) + # It should not be possible to create a SimplePage under a BusinessChild + self.assertCanNotCreateAt(SimplePage, BusinessChild) + + # This should raise, as it *is not* possible + with self.assertRaises(AssertionError): + self.assertCanCreateAt(SimplePage, BusinessChild) + # This should raise, as it *is* possible + with self.assertRaises(AssertionError): + self.assertCanNotCreateAt(EventIndex, EventPage) + + def test_assert_can_create(self): + + self.assertFalse(EventIndex.objects.exists()) + self.assertCanCreate(self.root, EventIndex, { + 'title': 'Event Index', + 'intro': '

Event intro

'}) + self.assertTrue(EventIndex.objects.exists()) + + self.assertCanCreate(self.root, StreamPage, { + 'title': 'WebDev42', + 'body': json.dumps([ + {'type': 'text', 'value': 'Some text'}, + {'type': 'rich_text', 'value': '

Some rich text

'}, + ])}) + + def test_assert_can_create_subpage_rules(self): + simple_page = SimplePage(title='Simple Page', slug='simple') + self.root.add_child(instance=simple_page) + # This should raise an error, as a BusinessChild can not be created under a SimplePage + with self.assertRaisesRegexp(AssertionError, r'Can not create a tests.businesschild under a tests.simplepage'): + self.assertCanCreate(simple_page, BusinessChild, {}) + + def test_assert_can_create_validation_error(self): + # This should raise some validation errors, complaining about missing + # title and slug fields + with self.assertRaisesRegexp(AssertionError, r'\bslug:\n[\s\S]*\btitle:\n'): + self.assertCanCreate(self.root, SimplePage, {})