Split up wagtail.admin.tests.test_pages_views

This commit is contained in:
Matt Westcott 2019-09-06 20:36:06 +01:00 committed by Matt Westcott
parent b9cfc24b37
commit 92aeaf00ff
18 changed files with 5584 additions and 5452 deletions

View file

View file

@ -0,0 +1,20 @@
from django.test import TestCase
from django.urls import reverse
from wagtail.tests.utils import WagtailTestUtils
class TestContentTypeUse(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def setUp(self):
self.user = self.login()
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,447 @@
from django import VERSION as DJANGO_VERSION
from django.contrib.auth.models import Group, Permission
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from wagtail.core.models import GroupPagePermission, Page
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils
class TestPageCopy(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Create a page
self.test_page = self.root_page.add_child(instance=SimplePage(
title="Hello world!",
slug='hello-world',
content="hello",
live=True,
has_unpublished_changes=False,
))
# Create a couple of child pages
self.test_child_page = self.test_page.add_child(instance=SimplePage(
title="Child page",
slug='child-page',
content="hello",
live=True,
has_unpublished_changes=True,
))
self.test_unpublished_child_page = self.test_page.add_child(instance=SimplePage(
title="Unpublished Child page",
slug='unpublished-child-page',
content="hello",
live=False,
has_unpublished_changes=True,
))
# Login
self.user = self.login()
def test_page_copy(self):
response = self.client.get(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )))
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/copy.html')
# Make sure all fields are in the form
self.assertContains(response, "New title")
self.assertContains(response, "New slug")
self.assertContains(response, "New parent page")
self.assertContains(response, "Copy subpages")
self.assertContains(response, "Publish copies")
def test_page_copy_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 copy page
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world',
'new_parent_page': str(self.test_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# A user with no page permissions at all should be redirected to the admin home
self.assertRedirects(response, reverse('wagtailadmin_home'))
# A user with page permissions, but not add permission at the destination,
# should receive a form validation error
publishers = Group.objects.create(name='Publishers')
GroupPagePermission.objects.create(
group=publishers, page=self.root_page, permission_type='publish'
)
self.user.groups.add(publishers)
self.user.save()
# Get copy page
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world',
'new_parent_page': str(self.test_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
form = response.context['form']
self.assertFalse(form.is_valid())
self.assertTrue('new_parent_page' in form.errors)
def test_page_copy_post(self):
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world-2',
'new_parent_page': str(self.root_page.id),
'copy_subpages': False,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Get copy
page_copy = self.root_page.get_children().filter(slug='hello-world-2').first()
# Check that the copy exists
self.assertNotEqual(page_copy, None)
# Check that the copy is not live
self.assertFalse(page_copy.live)
self.assertTrue(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were not copied
self.assertEqual(page_copy.get_children().count(), 0)
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
def test_page_copy_post_copy_subpages(self):
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world-2',
'new_parent_page': str(self.root_page.id),
'copy_subpages': True,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Get copy
page_copy = self.root_page.get_children().filter(slug='hello-world-2').first()
# Check that the copy exists
self.assertNotEqual(page_copy, None)
# Check that the copy is not live
self.assertFalse(page_copy.live)
self.assertTrue(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were copied
self.assertEqual(page_copy.get_children().count(), 2)
# Check the the child pages
# Neither of them should be live
child_copy = page_copy.get_children().filter(slug='child-page').first()
self.assertNotEqual(child_copy, None)
self.assertFalse(child_copy.live)
self.assertTrue(child_copy.has_unpublished_changes)
unpublished_child_copy = page_copy.get_children().filter(slug='unpublished-child-page').first()
self.assertNotEqual(unpublished_child_copy, None)
self.assertFalse(unpublished_child_copy.live)
self.assertTrue(unpublished_child_copy.has_unpublished_changes)
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
def test_page_copy_post_copy_subpages_publish_copies(self):
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world-2',
'new_parent_page': str(self.root_page.id),
'copy_subpages': True,
'publish_copies': True,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Get copy
page_copy = self.root_page.get_children().filter(slug='hello-world-2').first()
# Check that the copy exists
self.assertNotEqual(page_copy, None)
# Check that the copy is live
self.assertTrue(page_copy.live)
self.assertFalse(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were copied
self.assertEqual(page_copy.get_children().count(), 2)
# Check the the child pages
# The child_copy should be live but the unpublished_child_copy shouldn't
child_copy = page_copy.get_children().filter(slug='child-page').first()
self.assertNotEqual(child_copy, None)
self.assertTrue(child_copy.live)
self.assertTrue(child_copy.has_unpublished_changes)
unpublished_child_copy = page_copy.get_children().filter(slug='unpublished-child-page').first()
self.assertNotEqual(unpublished_child_copy, None)
self.assertFalse(unpublished_child_copy.live)
self.assertTrue(unpublished_child_copy.has_unpublished_changes)
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
def test_page_copy_post_new_parent(self):
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world-2',
'new_parent_page': str(self.test_child_page.id),
'copy_subpages': False,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the new parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.test_child_page.id, )))
# Check that the page was copied to the correct place
self.assertTrue(Page.objects.filter(slug='hello-world-2').first().get_parent(), self.test_child_page)
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
def test_page_copy_post_existing_slug_within_same_parent_page(self):
# This tests the existing slug checking on page copy when not changing the parent page
# Attempt to copy the page but forget to change the slug
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world',
'new_parent_page': str(self.root_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(
response,
'form',
'new_slug',
"This slug is already in use within the context of its parent page \"Welcome to your new Wagtail site!\""
)
def test_page_copy_post_and_subpages_to_same_tree_branch(self):
# This tests that a page cannot be copied into itself when copying subpages
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world',
'new_parent_page': str(self.test_child_page.id),
'copy_subpages': True,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id,)), post_data)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(
response, 'form', 'new_parent_page', "You cannot copy a page into itself when copying subpages"
)
def test_page_copy_post_existing_slug_to_another_parent_page(self):
# This tests the existing slug checking on page copy when changing the parent page
# Attempt to copy the page and changed the parent page
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world',
'new_parent_page': str(self.test_child_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.test_child_page.id, )))
def test_page_copy_post_invalid_slug(self):
# Attempt to copy the page but set an invalid slug string
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello world!',
'new_parent_page': str(self.root_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
if DJANGO_VERSION >= (3, 0):
self.assertFormError(
response, 'form', 'new_slug', "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens."
)
else:
self.assertFormError(
response, 'form', 'new_slug', "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."
)
def test_page_copy_no_publish_permission(self):
# Turn user into an editor who can add pages but not publish them
self.user.is_superuser = False
self.user.groups.add(
Group.objects.get(name="Editors"),
)
self.user.save()
# Get copy page
response = self.client.get(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )))
# The user should have access to the copy page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/copy.html')
# Make sure the "publish copies" field is hidden
self.assertNotContains(response, "Publish copies")
def test_page_copy_no_publish_permission_post_copy_subpages_publish_copies(self):
# This tests that unprivileged users cannot publish copied pages even if they hack their browser
# Turn user into an editor who can add pages but not publish them
self.user.is_superuser = False
self.user.groups.add(
Group.objects.get(name="Editors"),
)
self.user.save()
# Post
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world-2',
'new_parent_page': str(self.root_page.id),
'copy_subpages': True,
'publish_copies': True,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Get copy
page_copy = self.root_page.get_children().filter(slug='hello-world-2').first()
# Check that the copy exists
self.assertNotEqual(page_copy, None)
# Check that the copy is not live
self.assertFalse(page_copy.live)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were copied
self.assertEqual(page_copy.get_children().count(), 2)
# Check the the child pages
# Neither of them should be live
child_copy = page_copy.get_children().filter(slug='child-page').first()
self.assertNotEqual(child_copy, None)
self.assertFalse(child_copy.live)
unpublished_child_copy = page_copy.get_children().filter(slug='unpublished-child-page').first()
self.assertNotEqual(unpublished_child_copy, None)
self.assertFalse(unpublished_child_copy.live)
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
def test_before_copy_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('before_copy_page', hook_func):
response = self.client.get(reverse('wagtailadmin_pages:copy', args=(self.test_page.id,)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_copy_page_hook_post(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('before_copy_page', hook_func):
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world-2',
'new_parent_page': str(self.root_page.id),
'copy_subpages': False,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id,)), post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should not be copied
self.assertFalse(Page.objects.filter(title="Hello world 2").exists())
def test_after_copy_page_hook(self):
def hook_func(request, page, new_page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
self.assertIsInstance(new_page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('after_copy_page', hook_func):
post_data = {
'new_title': "Hello world 2",
'new_slug': 'hello-world-2',
'new_parent_page': str(self.root_page.id),
'copy_subpages': False,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id,)), post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should be copied
self.assertTrue(Page.objects.filter(title="Hello world 2").exists())

View file

@ -0,0 +1,881 @@
import datetime
from unittest import mock
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.core import mail
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from wagtail.admin.tests.pages.timestamps import submittable_timestamp
from wagtail.core.models import GroupPagePermission, Page, PageRevision
from wagtail.core.signals import page_published
from wagtail.tests.testapp.models import (
BusinessChild, BusinessIndex, BusinessSubIndex, DefaultStreamPage, SimplePage,
SingletonPage, SingletonPageViaMaxCount, StandardChild, StandardIndex)
from wagtail.tests.utils import WagtailTestUtils
class TestPageCreation(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Login
self.user = self.login()
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)
self.assertContains(response, "Simple page")
target_url = reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id))
self.assertContains(response, 'href="%s"' % target_url)
# List of available page types should not contain pages with is_creatable = False
self.assertNotContains(response, "MTI base page")
# List of available page types should not contain abstract pages
self.assertNotContains(response, "Abstract page")
# List of available page types should not contain pages whose parent_page_types forbid it
self.assertNotContains(response, "Business child")
def test_add_subpage_with_subpage_types(self):
# Add a BusinessIndex to test business rules in
business_index = BusinessIndex(
title="Hello world!",
slug="hello-world",
)
self.root_page.add_child(instance=business_index)
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(business_index.id, )))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Business child")
# List should not contain page types not in the subpage_types list
self.assertNotContains(response, "Simple page")
def test_add_subpage_with_one_valid_subpage_type(self):
# Add a BusinessSubIndex to test business rules in
business_index = BusinessIndex(
title="Hello world!",
slug="hello-world",
)
self.root_page.add_child(instance=business_index)
business_subindex = BusinessSubIndex(
title="Hello world!",
slug="hello-world",
)
business_index.add_child(instance=business_subindex)
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(business_subindex.id, )))
# Should be redirected to the 'add' page for BusinessChild, the only valid subpage type
self.assertRedirects(
response,
reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', business_subindex.id))
)
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 received 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)
def test_add_subpage_with_next_param(self):
response = self.client.get(
reverse('wagtailadmin_pages:add_subpage', args=(self.root_page.id, )),
{'next': '/admin/users/'}
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Simple page")
target_url = reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id))
self.assertContains(response, 'href="%s?next=/admin/users/"' % target_url)
def test_create_simplepage(self):
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<a href="#tab-content" class="active">Content</a>')
self.assertContains(response, '<a href="#tab-promote" class="">Promote</a>')
# test register_page_action_menu_item hook
self.assertContains(response, '<input type="submit" name="action-panic" value="Panic!" class="button" />')
self.assertContains(response, 'testapp/js/siren.js')
# test construct_page_action_menu hook
self.assertContains(response, '<input type="submit" name="action-relax" value="Relax." class="button" />')
def test_create_multipart(self):
"""
Test checks if 'enctype="multipart/form-data"' is added and only to forms that require multipart encoding.
"""
# check for SimplePage where is no file field
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'enctype="multipart/form-data"')
self.assertTemplateUsed(response, 'wagtailadmin/pages/create.html')
# check for FilePage which has file field
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'filepage', self.root_page.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'enctype="multipart/form-data"')
def test_create_page_without_promote_tab(self):
"""
Test that the Promote tab is not rendered for page classes that define it as empty
"""
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'standardindex', self.root_page.id))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<a href="#tab-content" class="active">Content</a>')
self.assertNotContains(response, '<a href="#tab-promote" class="">Promote</a>')
def test_create_page_with_custom_tabs(self):
"""
Test that custom edit handlers are rendered
"""
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.root_page.id))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<a href="#tab-content" class="active">Content</a>')
self.assertContains(response, '<a href="#tab-promote" class="">Promote</a>')
self.assertContains(response, '<a href="#tab-dinosaurs" class="">Dinosaurs</a>')
def test_create_page_with_non_model_field(self):
"""
Test that additional fields defined on the form rather than the model are accepted and rendered
"""
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'formclassadditionalfieldpage', self.root_page.id)))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/create.html')
self.assertContains(response, "Enter SMS authentication code")
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:add', args=('tests', 'simplepage', self.root_page.id, )))
# Check that the user received a 403 response
self.assertEqual(response.status_code, 403)
def test_cannot_create_page_with_is_creatable_false(self):
# tests.MTIBasePage has is_creatable=False, so attempting to add a new one
# should fail with permission denied
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'mtibasepage', self.root_page.id))
)
self.assertEqual(response.status_code, 403)
def test_cannot_create_page_when_can_create_at_returns_false(self):
# issue #2892
# Check that creating a second SingletonPage results in a permission
# denied error.
# SingletonPage overrides the can_create_at method to make it return
# False if another SingletonPage already exists.
# The Page model now has a max_count attribute (issue 4841),
# but we are leaving this test in place to cover existing behaviour and
# ensure it does not break any code doing this in the wild.
add_url = reverse('wagtailadmin_pages:add', args=[
SingletonPage._meta.app_label, SingletonPage._meta.model_name, self.root_page.pk])
# A single singleton page should be creatable
self.assertTrue(SingletonPage.can_create_at(self.root_page))
response = self.client.get(add_url)
self.assertEqual(response.status_code, 200)
# Create a singleton page
self.root_page.add_child(instance=SingletonPage(
title='singleton', slug='singleton'))
# A second singleton page should not be creatable
self.assertFalse(SingletonPage.can_create_at(self.root_page))
response = self.client.get(add_url)
self.assertEqual(response.status_code, 403)
def test_cannot_create_singleton_page_with_max_count(self):
# Check that creating a second SingletonPageViaMaxCount results in a permission
# denied error.
# SingletonPageViaMaxCount uses the max_count attribute to limit the number of
# instance it can have.
add_url = reverse('wagtailadmin_pages:add', args=[
SingletonPageViaMaxCount._meta.app_label, SingletonPageViaMaxCount._meta.model_name, self.root_page.pk])
# A single singleton page should be creatable
self.assertTrue(SingletonPageViaMaxCount.can_create_at(self.root_page))
response = self.client.get(add_url)
self.assertEqual(response.status_code, 200)
# Create a singleton page
self.root_page.add_child(instance=SingletonPageViaMaxCount(
title='singleton', slug='singleton'))
# A second singleton page should not be creatable
self.assertFalse(SingletonPageViaMaxCount.can_create_at(self.root_page))
response = self.client.get(add_url)
self.assertEqual(response.status_code, 403)
def test_cannot_create_page_with_wrong_parent_page_types(self):
# tests.BusinessChild has limited parent_page_types, so attempting to add
# a new one at the root level should fail with permission denied
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.root_page.id))
)
self.assertEqual(response.status_code, 403)
def test_cannot_create_page_with_wrong_subpage_types(self):
# Add a BusinessIndex to test business rules in
business_index = BusinessIndex(
title="Hello world!",
slug="hello-world",
)
self.root_page.add_child(instance=business_index)
# BusinessIndex has limited subpage_types, so attempting to add a SimplePage
# underneath it should fail with permission denied
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', business_index.id))
)
self.assertEqual(response.status_code, 403)
def test_create_simplepage_post(self):
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)),
post_data
)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
# Should be redirected to edit page
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(page.id, )))
self.assertEqual(page.title, post_data['title'])
self.assertEqual(page.draft_title, post_data['title'])
self.assertIsInstance(page, SimplePage)
self.assertFalse(page.live)
self.assertFalse(page.first_published_at)
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
def test_create_simplepage_scheduled(self):
go_live_at = timezone.now() + datetime.timedelta(days=1)
expire_at = timezone.now() + datetime.timedelta(days=2)
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'go_live_at': submittable_timestamp(go_live_at),
'expire_at': submittable_timestamp(expire_at),
}
response = self.client.post(
reverse('wagtailadmin_pages:add', 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 the scheduled times
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
self.assertEqual(page.go_live_at.date(), go_live_at.date())
self.assertEqual(page.expire_at.date(), expire_at.date())
self.assertEqual(page.expired, False)
self.assertTrue(page.status_string, "draft")
# No revisions with approved_go_live_at
self.assertFalse(PageRevision.objects.filter(page=page).exclude(approved_go_live_at__isnull=True).exists())
def test_create_simplepage_scheduled_go_live_before_expiry(self):
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'go_live_at': submittable_timestamp(timezone.now() + datetime.timedelta(days=2)),
'expire_at': submittable_timestamp(timezone.now() + datetime.timedelta(days=1)),
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(response, 'form', 'go_live_at', "Go live date/time must be before expiry date/time")
self.assertFormError(response, 'form', 'expire_at', "Go live date/time must be before expiry date/time")
# form should be marked as having unsaved changes for the purposes of the dirty-forms warning
self.assertContains(response, "alwaysDirty: true")
def test_create_simplepage_scheduled_expire_in_the_past(self):
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'expire_at': submittable_timestamp(timezone.now() + datetime.timedelta(days=-1)),
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(response, 'form', 'expire_at', "Expiry date/time must be in the future")
# form should be marked as having unsaved changes for the purposes of the dirty-forms warning
self.assertContains(response, "alwaysDirty: true")
def test_create_simplepage_post_publish(self):
# Connect a mock signal handler to page_published signal
mock_handler = mock.MagicMock()
page_published.connect(mock_handler)
# Post
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'action-publish': "Publish",
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
# Should be redirected to explorer
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
self.assertEqual(page.title, post_data['title'])
self.assertEqual(page.draft_title, post_data['title'])
self.assertIsInstance(page, SimplePage)
self.assertTrue(page.live)
self.assertTrue(page.first_published_at)
# Check that the page_published signal was fired
self.assertEqual(mock_handler.call_count, 1)
mock_call = mock_handler.mock_calls[0][2]
self.assertEqual(mock_call['sender'], page.specific_class)
self.assertEqual(mock_call['instance'], page)
self.assertIsInstance(mock_call['instance'], page.specific_class)
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
def test_create_simplepage_post_publish_scheduled(self):
go_live_at = timezone.now() + datetime.timedelta(days=1)
expire_at = timezone.now() + datetime.timedelta(days=2)
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'action-publish': "Publish",
'go_live_at': submittable_timestamp(go_live_at),
'expire_at': submittable_timestamp(expire_at),
}
response = self.client.post(
reverse('wagtailadmin_pages:add', 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.go_live_at.date(), go_live_at.date())
self.assertEqual(page.expire_at.date(), expire_at.date())
self.assertEqual(page.expired, False)
# A revision with approved_go_live_at should exist now
self.assertTrue(PageRevision.objects.filter(page=page).exclude(approved_go_live_at__isnull=True).exists())
# But Page won't be live
self.assertFalse(page.live)
self.assertFalse(page.first_published_at)
self.assertTrue(page.status_string, "scheduled")
def test_create_simplepage_post_submit(self):
# Create a moderator user for testing email
get_user_model().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:add', args=('tests', 'simplepage', self.root_page.id)), post_data
)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
# Should be redirected to explorer
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
self.assertEqual(page.title, post_data['title'])
self.assertIsInstance(page, SimplePage)
self.assertFalse(page.live)
self.assertFalse(page.first_published_at)
# 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_existing_slug(self):
# This tests the existing slug checking on page save
# Create a page
self.child_page = SimplePage(title="Hello world!", slug="hello-world", content="hello")
self.root_page.add_child(instance=self.child_page)
# Attempt to create a new one with the same slug
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'action-publish': "Publish",
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(response, 'form', 'slug', "This slug is already in use")
# form should be marked as having unsaved changes for the purposes of the dirty-forms warning
self.assertContains(response, "alwaysDirty: true")
def test_create_nonexistantparent(self):
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', 100000)))
self.assertEqual(response.status_code, 404)
def test_create_nonpagetype(self):
response = self.client.get(
reverse('wagtailadmin_pages:add', 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",
}
preview_url = reverse('wagtailadmin_pages:preview_on_add',
args=('tests', 'simplepage', self.root_page.id))
response = self.client.post(preview_url, post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content.decode(), {'is_valid': True})
response = self.client.get(preview_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'tests/simple_page.html')
self.assertContains(response, "New page!")
# Check that the treebeard attributes were set correctly on the page object
self.assertEqual(response.context['self'].depth, self.root_page.depth + 1)
self.assertTrue(response.context['self'].path.startswith(self.root_page.path))
self.assertEqual(response.context['self'].get_parent(), self.root_page)
def test_whitespace_titles(self):
post_data = {
'title': " ", # Single space on purpose
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
)
# Check that a form error was raised
self.assertFormError(response, 'form', 'title', "This field is required.")
def test_whitespace_titles_with_tab(self):
post_data = {
'title': "\t", # Single space on purpose
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Check that a form error was raised
self.assertFormError(response, 'form', 'title', "This field is required.")
def test_whitespace_titles_with_tab_in_seo_title(self):
post_data = {
'title': "Hello",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
'seo_title': '\t'
}
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Should be successful, as seo_title is not required
self.assertEqual(response.status_code, 302)
# The tab should be automatically stripped from the seo_title
page = Page.objects.order_by('-id').first()
self.assertEqual(page.seo_title, '')
def test_whitespace_is_stripped_from_titles(self):
post_data = {
'title': " Hello ",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
'seo_title': ' hello SEO '
}
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Should be successful, as both title and seo_title are non-empty after stripping
self.assertEqual(response.status_code, 302)
# Whitespace should be automatically stripped from title and seo_title
page = Page.objects.order_by('-id').first()
self.assertEqual(page.title, 'Hello')
self.assertEqual(page.draft_title, 'Hello')
self.assertEqual(page.seo_title, 'hello SEO')
def test_long_slug(self):
post_data = {
'title': "Hello world",
'content': "Some content",
'slug': 'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world-'
'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world-'
'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world-'
'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world',
'action-submit': "Submit",
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
)
# Check that a form error was raised
self.assertEqual(response.status_code, 200)
self.assertFormError(response, 'form', 'slug', "Ensure this value has at most 255 characters (it has 287).")
def test_before_create_page_hook(self):
def hook_func(request, parent_page, page_class):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(parent_page.id, self.root_page.id)
self.assertEqual(page_class, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('before_create_page', hook_func):
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id))
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_create_page_hook_post(self):
def hook_func(request, parent_page, page_class):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(parent_page.id, self.root_page.id)
self.assertEqual(page_class, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('before_create_page', hook_func):
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)),
post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should not be created
self.assertFalse(Page.objects.filter(title="New page!").exists())
def test_after_create_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('after_create_page', hook_func):
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)),
post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should be created
self.assertTrue(Page.objects.filter(title="New page!").exists())
class TestPerRequestEditHandler(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
GroupPagePermission.objects.create(
group=Group.objects.get(name="Site-wide editors"),
page=self.root_page, permission_type='add'
)
def test_create_page_with_per_request_custom_edit_handlers(self):
"""
Test that per-request custom behaviour in edit handlers is honoured
"""
# non-superusers should not see secret_data
logged_in = self.client.login(username='siteeditor', password='password')
self.assertTrue(logged_in)
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'secretpage', self.root_page.id))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, '"boring_data"')
self.assertNotContains(response, '"secret_data"')
# superusers should see secret_data
logged_in = self.client.login(username='superuser', password='password')
self.assertTrue(logged_in)
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'secretpage', self.root_page.id))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, '"boring_data"')
self.assertContains(response, '"secret_data"')
class TestSubpageBusinessRules(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add standard page (allows subpages of any type)
self.standard_index = StandardIndex()
self.standard_index.title = "Standard Index"
self.standard_index.slug = "standard-index"
self.root_page.add_child(instance=self.standard_index)
# Add business page (allows BusinessChild and BusinessSubIndex as subpages)
self.business_index = BusinessIndex()
self.business_index.title = "Business Index"
self.business_index.slug = "business-index"
self.root_page.add_child(instance=self.business_index)
# Add business child (allows no subpages)
self.business_child = BusinessChild()
self.business_child.title = "Business Child"
self.business_child.slug = "business-child"
self.business_index.add_child(instance=self.business_child)
# Add business subindex (allows only BusinessChild as subpages)
self.business_subindex = BusinessSubIndex()
self.business_subindex.title = "Business Subindex"
self.business_subindex.slug = "business-subindex"
self.business_index.add_child(instance=self.business_subindex)
# Login
self.login()
def test_standard_subpage(self):
add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.standard_index.id, ))
# explorer should contain a link to 'add child page'
response = self.client.get(reverse('wagtailadmin_explore', args=(self.standard_index.id, )))
self.assertEqual(response.status_code, 200)
self.assertContains(response, add_subpage_url)
# add_subpage should give us choices of StandardChild, and BusinessIndex.
# BusinessSubIndex and BusinessChild are not allowed
response = self.client.get(add_subpage_url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, StandardChild.get_verbose_name())
self.assertContains(response, BusinessIndex.get_verbose_name())
self.assertNotContains(response, BusinessSubIndex.get_verbose_name())
self.assertNotContains(response, BusinessChild.get_verbose_name())
def test_business_subpage(self):
add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.business_index.id, ))
# explorer should contain a link to 'add child page'
response = self.client.get(reverse('wagtailadmin_explore', args=(self.business_index.id, )))
self.assertEqual(response.status_code, 200)
self.assertContains(response, add_subpage_url)
# add_subpage should give us a cut-down set of page types to choose
response = self.client.get(add_subpage_url)
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, StandardIndex.get_verbose_name())
self.assertNotContains(response, StandardChild.get_verbose_name())
self.assertContains(response, BusinessSubIndex.get_verbose_name())
self.assertContains(response, BusinessChild.get_verbose_name())
def test_business_child_subpage(self):
add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.business_child.id, ))
# explorer should not contain a link to 'add child page', as this page doesn't accept subpages
response = self.client.get(reverse('wagtailadmin_explore', args=(self.business_child.id, )))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, add_subpage_url)
# this also means that fetching add_subpage is blocked at the permission-check level
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.business_child.id, )))
self.assertEqual(response.status_code, 403)
def test_cannot_add_invalid_subpage_type(self):
# cannot add StandardChild as a child of BusinessIndex, as StandardChild is not present in subpage_types
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.business_index.id))
)
self.assertEqual(response.status_code, 403)
# likewise for BusinessChild which has an empty subpage_types list
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.business_child.id))
)
self.assertEqual(response.status_code, 403)
# cannot add BusinessChild to StandardIndex, as BusinessChild restricts is parent page types
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.standard_index.id))
)
self.assertEqual(response.status_code, 403)
# but we can add a BusinessChild to BusinessIndex
response = self.client.get(
reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.business_index.id))
)
self.assertEqual(response.status_code, 200)
def test_not_prompted_for_page_type_when_only_one_choice(self):
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.business_subindex.id, )))
# BusinessChild is the only valid subpage type of BusinessSubIndex, so redirect straight there
self.assertRedirects(
response, reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.business_subindex.id))
)
class TestInlinePanelMedia(TestCase, WagtailTestUtils):
"""
Test that form media required by InlinePanels is correctly pulled in to the edit page
"""
def test_inline_panel_media(self):
homepage = Page.objects.get(id=2)
self.login()
# simplepage does not need draftail...
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', homepage.id)))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'wagtailadmin/js/draftail.js')
# but sectionedrichtextpage does
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'sectionedrichtextpage', homepage.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'wagtailadmin/js/draftail.js')
class TestInlineStreamField(TestCase, WagtailTestUtils):
"""
Test that streamfields inside an inline child work
"""
def test_inline_streamfield(self):
homepage = Page.objects.get(id=2)
self.login()
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'inlinestreampage', homepage.id)))
self.assertEqual(response.status_code, 200)
# response should include HTML declarations for streamfield child blocks
self.assertContains(response, '<div id="__PREFIX__-container" aria-hidden="false">')
class TestIssue2994(TestCase, WagtailTestUtils):
"""
In contrast to most "standard" form fields, StreamField form widgets generally won't
provide a postdata field with a name exactly matching the field name. To prevent Django
from wrongly interpreting this as the field being omitted from the form,
we need to provide a custom value_omitted_from_data method.
"""
def setUp(self):
self.root_page = Page.objects.get(id=2)
self.user = self.login()
def test_page_edit_post_publish_url(self):
# Post
post_data = {
'title': "Issue 2994 test",
'slug': 'issue-2994-test',
'body-count': '1',
'body-0-deleted': '',
'body-0-order': '0',
'body-0-type': 'text',
'body-0-value': 'hello world',
'action-publish': "Publish",
}
self.client.post(
reverse('wagtailadmin_pages:add', args=('tests', 'defaultstreampage', self.root_page.id)), post_data
)
new_page = DefaultStreamPage.objects.get(slug='issue-2994-test')
self.assertEqual(1, len(new_page.body))
self.assertEqual('hello world', new_page.body[0].value)

View file

@ -0,0 +1,92 @@
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from wagtail.admin.views.home import RecentEditsPanel
from wagtail.core.models import Page
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils
class TestRecentEditsPanel(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child page
child_page = SimplePage(
title="Hello world!",
slug="hello-world",
content="Some content here",
)
self.root_page.add_child(instance=child_page)
child_page.save_revision().publish()
self.child_page = SimplePage.objects.get(id=child_page.id)
get_user_model().objects.create_superuser(username='alice', email='alice@email.com', password='password')
get_user_model().objects.create_superuser(username='bob', email='bob@email.com', password='password')
def change_something(self, title):
post_data = {'title': title, '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 edit page
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )))
# 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 go_to_dashboard_response(self):
response = self.client.get(reverse('wagtailadmin_home'))
self.assertEqual(response.status_code, 200)
return response
def test_your_recent_edits(self):
# Login as Bob
self.client.login(username='bob', password='password')
# Bob hasn't edited anything yet
response = self.client.get(reverse('wagtailadmin_home'))
self.assertNotIn('Your most recent edits', response.content.decode('utf-8'))
# Login as Alice
self.client.logout()
self.client.login(username='alice', password='password')
# Alice changes something
self.change_something("Alice's edit")
# Edit should show up on dashboard
response = self.go_to_dashboard_response()
self.assertIn('Your most recent edits', response.content.decode('utf-8'))
# Bob changes something
self.client.login(username='bob', password='password')
self.change_something("Bob's edit")
# Edit shows up on Bobs dashboard
response = self.go_to_dashboard_response()
self.assertIn('Your most recent edits', response.content.decode('utf-8'))
# Login as Alice again
self.client.logout()
self.client.login(username='alice', password='password')
# Alice's dashboard should still list that first edit
response = self.go_to_dashboard_response()
self.assertIn('Your most recent edits', response.content.decode('utf-8'))
def test_panel(self):
"""Test if the panel actually returns expected pages """
self.client.login(username='bob', password='password')
# change a page
self.change_something("Bob's edit")
# set a user to 'mock' a request
self.client.user = get_user_model().objects.get(email='bob@email.com')
# get the panel to get the last edits
panel = RecentEditsPanel(self.client)
# check if the revision is the revision of edited Page
self.assertEqual(panel.last_edits[0][0].page, Page.objects.get(pk=self.child_page.id))
# check if the page in this list is the specific page of this revision
self.assertEqual(panel.last_edits[0][1], Page.objects.get(pk=self.child_page.id).specific)

View file

@ -0,0 +1,204 @@
from unittest import mock
from django.contrib.auth.models import Permission
from django.db.models.signals import post_delete, pre_delete
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from wagtail.core.models import Page
from wagtail.core.signals import page_unpublished
from wagtail.tests.testapp.models import SimplePage, StandardChild, StandardIndex
from wagtail.tests.utils import WagtailTestUtils
class TestPageDelete(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child page
self.child_page = SimplePage(title="Hello world!", slug="hello-world", content="hello")
self.root_page.add_child(instance=self.child_page)
# Add a page with child pages of its own
self.child_index = StandardIndex(title="Hello index", slug='hello-index')
self.root_page.add_child(instance=self.child_index)
self.grandchild_page = StandardChild(title="Hello Kitty", slug='hello-kitty')
self.child_index.add_child(instance=self.grandchild_page)
# Login
self.user = self.login()
def test_page_delete(self):
response = self.client.get(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
# deletion should not actually happen on GET
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
def test_page_delete_specific_admin_title(self):
response = self.client.get(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
# The admin_display_title specific to ChildPage is shown on the delete confirmation page.
self.assertContains(response, self.child_page.get_admin_display_title())
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 received a 403 response
self.assertEqual(response.status_code, 403)
# Check that the deletion has not happened
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
def test_page_delete_post(self):
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
# Check that the page is gone
self.assertEqual(Page.objects.filter(path__startswith=self.root_page.path, slug='hello-world').count(), 0)
# Check that the page_unpublished signal was fired
self.assertEqual(mock_handler.call_count, 1)
mock_call = mock_handler.mock_calls[0][2]
self.assertEqual(mock_call['sender'], self.child_page.specific_class)
self.assertEqual(mock_call['instance'], self.child_page)
self.assertIsInstance(mock_call['instance'], self.child_page.specific_class)
def test_page_delete_notlive_post(self):
# Same as above, but this makes sure the page_unpublished signal is not fired
# when if the page is not live when it is deleted
# Unpublish the page
self.child_page.live = False
self.child_page.save()
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
# Check that the page is gone
self.assertEqual(Page.objects.filter(path__startswith=self.root_page.path, slug='hello-world').count(), 0)
# Check that the page_unpublished signal was not fired
self.assertEqual(mock_handler.call_count, 0)
def test_subpage_deletion(self):
# Connect mock signal handlers to page_unpublished, pre_delete and post_delete signals
unpublish_signals_received = []
pre_delete_signals_received = []
post_delete_signals_received = []
def page_unpublished_handler(sender, instance, **kwargs):
unpublish_signals_received.append((sender, instance.id))
def pre_delete_handler(sender, instance, **kwargs):
pre_delete_signals_received.append((sender, instance.id))
def post_delete_handler(sender, instance, **kwargs):
post_delete_signals_received.append((sender, instance.id))
page_unpublished.connect(page_unpublished_handler)
pre_delete.connect(pre_delete_handler)
post_delete.connect(post_delete_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_index.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# treebeard should report no consistency problems with the tree
self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
# Check that the page is gone
self.assertFalse(StandardIndex.objects.filter(id=self.child_index.id).exists())
self.assertFalse(Page.objects.filter(id=self.child_index.id).exists())
# Check that the subpage is also gone
self.assertFalse(StandardChild.objects.filter(id=self.grandchild_page.id).exists())
self.assertFalse(Page.objects.filter(id=self.grandchild_page.id).exists())
# Check that the signals were fired for both pages
self.assertIn((StandardIndex, self.child_index.id), unpublish_signals_received)
self.assertIn((StandardChild, self.grandchild_page.id), unpublish_signals_received)
self.assertIn((StandardIndex, self.child_index.id), pre_delete_signals_received)
self.assertIn((StandardChild, self.grandchild_page.id), pre_delete_signals_received)
self.assertIn((StandardIndex, self.child_index.id), post_delete_signals_received)
self.assertIn((StandardChild, self.grandchild_page.id), post_delete_signals_received)
def test_before_delete_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(page.id, self.child_page.id)
return HttpResponse("Overridden!")
with self.register_hook('before_delete_page', hook_func):
response = self.client.get(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_delete_page_hook_post(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(page.id, self.child_page.id)
return HttpResponse("Overridden!")
with self.register_hook('before_delete_page', hook_func):
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should not be deleted
self.assertTrue(Page.objects.filter(id=self.child_page.id).exists())
def test_after_delete_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(page.id, self.child_page.id)
return HttpResponse("Overridden!")
with self.register_hook('after_delete_page', hook_func):
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should be deleted
self.assertFalse(Page.objects.filter(id=self.child_page.id).exists())

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,544 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.core import paginator
from django.test import TestCase
from django.urls import reverse
from wagtail.admin.tests.pages.timestamps import local_datetime
from wagtail.core.models import GroupPagePermission, Page
from wagtail.tests.testapp.models import SimplePage, SingleEventPage, StandardIndex
from wagtail.tests.utils import WagtailTestUtils
class TestPageExplorer(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child page
self.child_page = SimplePage(
title="Hello world!",
slug="hello-world",
content="hello",
)
self.root_page.add_child(instance=self.child_page)
# more child pages to test ordering
self.old_page = StandardIndex(
title="Old page",
slug="old-page",
latest_revision_created_at=local_datetime(2010, 1, 1)
)
self.root_page.add_child(instance=self.old_page)
self.new_page = SimplePage(
title="New page",
slug="new-page",
content="hello",
latest_revision_created_at=local_datetime(2016, 1, 1)
)
self.root_page.add_child(instance=self.new_page)
# Login
self.user = self.login()
def test_explore(self):
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertEqual(self.root_page, response.context['parent_page'])
# child pages should be most recent first
# (with null latest_revision_created_at at the end)
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [self.new_page.id, self.old_page.id, self.child_page.id])
def test_explore_root(self):
response = self.client.get(reverse('wagtailadmin_explore_root'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertEqual(Page.objects.get(id=1), response.context['parent_page'])
self.assertTrue(response.context['pages'].paginator.object_list.filter(id=self.root_page.id).exists())
def test_explore_root_shows_icon(self):
response = self.client.get(reverse('wagtailadmin_explore_root'))
self.assertEqual(response.status_code, 200)
# Administrator (or user with add_site permission) should see the
# sites link with the icon-site icon
self.assertContains(
response,
("""<a href="/admin/sites/" class="icon icon-site" """
"""title="Sites menu"></a>""")
)
def test_ordering(self):
response = self.client.get(
reverse('wagtailadmin_explore', args=(self.root_page.id, )),
{'ordering': 'title'}
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertEqual(response.context['ordering'], 'title')
# child pages should be ordered by title
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [self.child_page.id, self.new_page.id, self.old_page.id])
def test_reverse_ordering(self):
response = self.client.get(
reverse('wagtailadmin_explore', args=(self.root_page.id, )),
{'ordering': '-title'}
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertEqual(response.context['ordering'], '-title')
# child pages should be ordered by title
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [self.old_page.id, self.new_page.id, self.child_page.id])
def test_ordering_by_last_revision_forward(self):
response = self.client.get(
reverse('wagtailadmin_explore', args=(self.root_page.id, )),
{'ordering': 'latest_revision_created_at'}
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertEqual(response.context['ordering'], 'latest_revision_created_at')
# child pages should be oldest revision first
# (with null latest_revision_created_at at the start)
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [self.child_page.id, self.old_page.id, self.new_page.id])
def test_invalid_ordering(self):
response = self.client.get(
reverse('wagtailadmin_explore', args=(self.root_page.id, )),
{'ordering': 'invalid_order'}
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertEqual(response.context['ordering'], '-latest_revision_created_at')
def test_reordering(self):
response = self.client.get(
reverse('wagtailadmin_explore', args=(self.root_page.id, )),
{'ordering': 'ord'}
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertEqual(response.context['ordering'], 'ord')
# child pages should be ordered by native tree order (i.e. by creation time)
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [self.child_page.id, self.old_page.id, self.new_page.id])
# Pages must not be paginated
self.assertNotIsInstance(response.context['pages'], paginator.Page)
def test_construct_explorer_page_queryset_hook(self):
# testapp implements a construct_explorer_page_queryset hook
# that only returns pages with a slug starting with 'hello'
# when the 'polite_pages_only' URL parameter is set
response = self.client.get(
reverse('wagtailadmin_explore', args=(self.root_page.id, )),
{'polite_pages_only': 'yes_please'}
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [self.child_page.id])
def test_construct_construct_page_listing_buttons_hook(self):
# testapp implements a construct_page_listing_buttons hook
# that add's an dummy button with the label 'Dummy Button' which points
# to '/dummy-button'
response = self.client.get(
reverse('wagtailadmin_explore', args=(self.root_page.id, )),
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
self.assertContains(response, 'Dummy Button')
self.assertContains(response, '/dummy-button')
def make_pages(self):
for i in range(150):
self.root_page.add_child(instance=SimplePage(
title="Page " + str(i),
slug="page-" + str(i),
content="hello",
))
def test_pagination(self):
self.make_pages()
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )), {'p': 2})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
# Check that we got the correct page
self.assertEqual(response.context['pages'].number, 2)
def test_pagination_invalid(self):
self.make_pages()
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )), {'p': 'Hello World!'})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
# Check that we got page one
self.assertEqual(response.context['pages'].number, 1)
def test_pagination_out_of_range(self):
self.make_pages()
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )), {'p': 99999})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
# Check that we got the last page
self.assertEqual(response.context['pages'].number, response.context['pages'].paginator.num_pages)
def test_listing_uses_specific_models(self):
# SingleEventPage has custom URL routing; the 'live' link in the listing
# should show the custom URL, which requires us to use the specific version
# of the class
self.new_event = SingleEventPage(
title="New event",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1)
)
self.root_page.add_child(instance=self.new_event)
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '/new-event/pointless-suffix/')
def make_event_pages(self, count):
for i in range(count):
self.root_page.add_child(instance=SingleEventPage(
title="New event " + str(i),
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1)
))
def test_exploring_uses_specific_page_with_custom_display_title(self):
# SingleEventPage has a custom get_admin_display_title method; explorer should
# show the custom title rather than the basic database one
self.make_event_pages(count=1)
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )))
self.assertContains(response, 'New event 0 (single event)')
new_event = SingleEventPage.objects.latest('pk')
response = self.client.get(reverse('wagtailadmin_explore', args=(new_event.id, )))
self.assertContains(response, 'New event 0 (single event)')
def test_ordering_less_than_100_pages_uses_specific_page_with_custom_display_title(self):
# Reorder view should also use specific pages
# (provided there are <100 pages in the listing, as this may be a significant
# performance hit on larger listings)
# There are 3 pages created in setUp, so 96 more add to a total of 99.
self.make_event_pages(count=96)
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )) + '?ordering=ord')
self.assertContains(response, 'New event 0 (single event)')
def test_ordering_100_or_more_pages_uses_generic_page_without_custom_display_title(self):
# There are 3 pages created in setUp, so 97 more add to a total of 100.
self.make_event_pages(count=97)
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )) + '?ordering=ord')
self.assertNotContains(response, 'New event 0 (single event)')
def test_parent_page_is_specific(self):
response = self.client.get(reverse('wagtailadmin_explore', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertIsInstance(response.context['parent_page'], SimplePage)
def test_explorer_no_perms(self):
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
admin = reverse('wagtailadmin_home')
self.assertRedirects(
self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, ))),
admin)
self.assertRedirects(
self.client.get(reverse('wagtailadmin_explore_root')), admin)
def test_explore_with_missing_page_model(self):
# Create a ContentType that doesn't correspond to a real model
missing_page_content_type = ContentType.objects.create(app_label='tests', model='missingpage')
# Turn /home/old-page/ into this content type
Page.objects.filter(id=self.old_page.id).update(content_type=missing_page_content_type)
# try to browse the the listing that contains the missing model
response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
# try to browse into the page itself
response = self.client.get(reverse('wagtailadmin_explore', args=(self.old_page.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/index.html')
class TestBreadcrumb(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def test_breadcrumb_uses_specific_titles(self):
self.user = self.login()
# get the explorer view for a subpage of a SimplePage
page = Page.objects.get(url_path='/home/secret-plans/steal-underpants/')
response = self.client.get(reverse('wagtailadmin_explore', args=(page.id, )))
# The breadcrumb should pick up SimplePage's overridden get_admin_display_title method
expected_url = reverse('wagtailadmin_explore', args=(Page.objects.get(url_path='/home/secret-plans/').id, ))
self.assertContains(response, """<li><a href="%s">Secret plans (simple page)</a></li>""" % expected_url)
class TestPageExplorerSignposting(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=1)
# Find page with an associated site
self.site_page = Page.objects.get(id=2)
# Add another top-level page (which will have no corresponding site record)
self.no_site_page = SimplePage(
title="Hello world!",
slug="hello-world",
content="hello",
)
self.root_page.add_child(instance=self.no_site_page)
# Tests for users that have both add-site permission, and explore permission at the given view;
# warning messages should include advice re configuring sites
def test_admin_at_root(self):
self.assertTrue(self.client.login(username='superuser', password='password'))
response = self.client.get(reverse('wagtailadmin_explore_root'))
self.assertEqual(response.status_code, 200)
# Administrator (or user with add_site permission) should get the full message
# about configuring sites
self.assertContains(
response,
(
"The root level is where you can add new sites to your Wagtail installation. "
"Pages created here will not be accessible at any URL until they are associated with a site."
)
)
self.assertContains(response, """<a href="/admin/sites/">Configure a site now.</a>""")
def test_admin_at_non_site_page(self):
self.assertTrue(self.client.login(username='superuser', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=(self.no_site_page.id, )))
self.assertEqual(response.status_code, 200)
# Administrator (or user with add_site permission) should get a warning about
# unroutable pages, and be directed to the site config area
self.assertContains(
response,
(
"There is no site set up for this location. "
"Pages created here will not be accessible at any URL until a site is associated with this location."
)
)
self.assertContains(response, """<a href="/admin/sites/">Configure a site now.</a>""")
def test_admin_at_site_page(self):
self.assertTrue(self.client.login(username='superuser', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=(self.site_page.id, )))
self.assertEqual(response.status_code, 200)
# There should be no warning message here
self.assertNotContains(response, "Pages created here will not be accessible")
# Tests for standard users that have explore permission at the given view;
# warning messages should omit advice re configuring sites
def test_nonadmin_at_root(self):
# Assign siteeditor permission over no_site_page, so that the deepest-common-ancestor
# logic allows them to explore root
GroupPagePermission.objects.create(
group=Group.objects.get(name="Site-wide editors"),
page=self.no_site_page, permission_type='add'
)
self.assertTrue(self.client.login(username='siteeditor', password='password'))
response = self.client.get(reverse('wagtailadmin_explore_root'))
self.assertEqual(response.status_code, 200)
# Non-admin should get a simple "create pages as children of the homepage" prompt
self.assertContains(
response,
"Pages created here will not be accessible at any URL. "
"To add pages to an existing site, create them as children of the homepage."
)
def test_nonadmin_at_non_site_page(self):
# Assign siteeditor permission over no_site_page
GroupPagePermission.objects.create(
group=Group.objects.get(name="Site-wide editors"),
page=self.no_site_page, permission_type='add'
)
self.assertTrue(self.client.login(username='siteeditor', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=(self.no_site_page.id, )))
self.assertEqual(response.status_code, 200)
# Non-admin should get a warning about unroutable pages
self.assertContains(
response,
(
"There is no site record for this location. "
"Pages created here will not be accessible at any URL."
)
)
def test_nonadmin_at_site_page(self):
self.assertTrue(self.client.login(username='siteeditor', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=(self.site_page.id, )))
self.assertEqual(response.status_code, 200)
# There should be no warning message here
self.assertNotContains(response, "Pages created here will not be accessible")
# Tests for users that have explore permission *somewhere*, but not at the view being tested;
# in all cases, they should be redirected to their explorable root
def test_bad_permissions_at_root(self):
# 'siteeditor' does not have permission to explore the root
self.assertTrue(self.client.login(username='siteeditor', password='password'))
response = self.client.get(reverse('wagtailadmin_explore_root'))
# Users without permission to explore here should be redirected to their explorable root.
self.assertEqual(
(response.status_code, response['Location']),
(302, reverse('wagtailadmin_explore', args=(self.site_page.pk, )))
)
def test_bad_permissions_at_non_site_page(self):
# 'siteeditor' does not have permission to explore no_site_page
self.assertTrue(self.client.login(username='siteeditor', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=(self.no_site_page.id, )))
# Users without permission to explore here should be redirected to their explorable root.
self.assertEqual(
(response.status_code, response['Location']),
(302, reverse('wagtailadmin_explore', args=(self.site_page.pk, )))
)
def test_bad_permissions_at_site_page(self):
# Adjust siteeditor's permission so that they have permission over no_site_page
# instead of site_page
Group.objects.get(name="Site-wide editors").page_permissions.update(page_id=self.no_site_page.id)
self.assertTrue(self.client.login(username='siteeditor', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=(self.site_page.id, )))
# Users without permission to explore here should be redirected to their explorable root.
self.assertEqual(
(response.status_code, response['Location']),
(302, reverse('wagtailadmin_explore', args=(self.no_site_page.pk, )))
)
class TestExplorablePageVisibility(TestCase, WagtailTestUtils):
"""
Test the way that the Explorable Pages functionality manifests within the Explorer.
This is isolated in its own test case because it requires a custom page tree and custom set of
users and groups.
The fixture sets up this page tree:
========================================================
ID Site Path
========================================================
1 /
2 testserver /home/
3 testserver /home/about-us/
4 example.com /example-home/
5 example.com /example-home/content/
6 example.com /example-home/content/page-1/
7 example.com /example-home/content/page-2/
9 example.com /example-home/content/page-2/child-1
8 example.com /example-home/other-content/
10 example2.com /home-2/
========================================================
Group 1 has explore and choose permissions rooted at testserver's homepage.
Group 2 has explore and choose permissions rooted at example.com's page-1.
Group 3 has explore and choose permissions rooted at example.com's other-content.
User "jane" is in Group 1.
User "bob" is in Group 2.
User "sam" is in Groups 1 and 2.
User "josh" is in Groups 2 and 3.
User "mary" is is no Groups, but she has the "access wagtail admin" permission.
User "superman" is an admin.
"""
fixtures = ['test_explorable_pages.json']
# Integration tests adapted from @coredumperror
def test_admin_can_explore_every_page(self):
self.assertTrue(self.client.login(username='superman', password='password'))
for page in Page.objects.all():
response = self.client.get(reverse('wagtailadmin_explore', args=[page.pk]))
self.assertEqual(response.status_code, 200)
def test_admin_sees_root_page_as_explorer_root(self):
self.assertTrue(self.client.login(username='superman', password='password'))
response = self.client.get(reverse('wagtailadmin_explore_root'))
self.assertEqual(response.status_code, 200)
# Administrator should see the full list of children of the Root page.
self.assertContains(response, "Welcome to testserver!")
self.assertContains(response, "Welcome to example.com!")
def test_admin_sees_breadcrumbs_up_to_root_page(self):
self.assertTrue(self.client.login(username='superman', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=[6]))
self.assertEqual(response.status_code, 200)
self.assertInHTML(
"""<li class="home"><a href="/admin/pages/" class="icon icon-site text-replace">Root</a></li>""",
str(response.content)
)
self.assertInHTML("""<li><a href="/admin/pages/4/">Welcome to example.com!</a></li>""", str(response.content))
self.assertInHTML("""<li><a href="/admin/pages/5/">Content</a></li>""", str(response.content))
def test_nonadmin_sees_breadcrumbs_up_to_cca(self):
self.assertTrue(self.client.login(username='josh', password='password'))
response = self.client.get(reverse('wagtailadmin_explore', args=[6]))
self.assertEqual(response.status_code, 200)
# While at "Page 1", Josh should see the breadcrumbs leading only as far back as the example.com homepage,
# since it's his Closest Common Ancestor.
self.assertInHTML(
"""<li class="home"><a href="/admin/pages/4/" class="icon icon-home text-replace">Home</a></li>""",
str(response.content)
)
self.assertInHTML("""<li><a href="/admin/pages/5/">Content</a></li>""", str(response.content))
# The page title shouldn't appear because it's the "home" breadcrumb.
self.assertNotContains(response, "Welcome to example.com!")
def test_admin_home_page_changes_with_permissions(self):
self.assertTrue(self.client.login(username='bob', password='password'))
response = self.client.get(reverse('wagtailadmin_home'))
self.assertEqual(response.status_code, 200)
# Bob should only see the welcome for example.com, not testserver
self.assertContains(response, "Welcome to the example.com Wagtail CMS")
self.assertNotContains(response, "testserver")
def test_breadcrumb_with_no_user_permissions(self):
self.assertTrue(self.client.login(username='mary', password='password'))
response = self.client.get(reverse('wagtailadmin_home'))
self.assertEqual(response.status_code, 200)
# Since Mary has no page permissions, she should not see the breadcrumb
self.assertNotContains(response, """<li class="home"><a href="/admin/pages/4/" class="icon icon-home text-replace">Home</a></li>""")

View file

@ -0,0 +1,383 @@
import logging
from itertools import chain
from unittest import mock
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.contrib.messages import constants as message_constants
from django.core import mail
from django.core.mail import EmailMultiAlternatives
from django.test import TestCase, override_settings
from django.urls import reverse
from wagtail.core.models import GroupPagePermission, Page, PageRevision
from wagtail.core.signals import page_published
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils
from wagtail.users.models import UserProfile
class TestApproveRejectModeration(TestCase, WagtailTestUtils):
def setUp(self):
self.submitter = get_user_model().objects.create_superuser(
username='submitter',
email='submitter@email.com',
password='password',
)
self.user = self.login()
# Create a page and submit it for moderation
root_page = Page.objects.get(id=2)
self.page = SimplePage(
title="Hello world!",
slug='hello-world',
content="hello",
live=False,
has_unpublished_changes=True,
)
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
"""
# Connect a mock signal handler to page_published signal
mock_handler = mock.MagicMock()
page_published.connect(mock_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(self.revision.id, )))
# Check that the user was redirected to the dashboard
self.assertRedirects(response, reverse('wagtailadmin_home'))
page = Page.objects.get(id=self.page.id)
# Page must be live
self.assertTrue(page.live, "Approving moderation failed to set live=True")
# Page should now have no unpublished changes
self.assertFalse(
page.has_unpublished_changes,
"Approving moderation failed to set has_unpublished_changes=False"
)
# Check that the page_published signal was fired
self.assertEqual(mock_handler.call_count, 1)
mock_call = mock_handler.mock_calls[0][2]
self.assertEqual(mock_call['sender'], self.page.specific_class)
self.assertEqual(mock_call['instance'], self.page)
self.assertIsInstance(mock_call['instance'], self.page.specific_class)
def test_approve_moderation_when_later_revision_exists(self):
self.page.title = "Goodbye world!"
self.page.save_revision(user=self.submitter, submitted_for_moderation=False)
response = self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(self.revision.id, )))
# Check that the user was redirected to the dashboard
self.assertRedirects(response, reverse('wagtailadmin_home'))
page = Page.objects.get(id=self.page.id)
# Page must be live
self.assertTrue(page.live, "Approving moderation failed to set live=True")
# Page content should be the submitted version, not the published one
self.assertEqual(page.title, "Hello world!")
# Page should still have unpublished changes
self.assertTrue(
page.has_unpublished_changes,
"has_unpublished_changes incorrectly cleared on approve_moderation when a later revision exists"
)
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, )))
# Check that the user received 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, )))
# Check that the user received 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, )))
# Check that the user was redirected to the dashboard
self.assertRedirects(response, reverse('wagtailadmin_home'))
# 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)
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, )))
# Check that the user received 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, )))
# Check that the user received 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 TestNotificationPreferences(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Login
self.user = self.login()
# Create two moderator users for testing 'submitted' email
User = get_user_model()
self.moderator = User.objects.create_superuser('moderator', 'moderator@email.com', 'password')
self.moderator2 = User.objects.create_superuser('moderator2', 'moderator2@email.com', 'password')
# Create a submitter for testing 'rejected' and 'approved' emails
self.submitter = User.objects.create_user('submitter', 'submitter@email.com', 'password')
# User profiles for moderator2 and the submitter
self.moderator2_profile = UserProfile.get_for_user(self.moderator2)
self.submitter_profile = UserProfile.get_for_user(self.submitter)
# Create a page and submit it for moderation
self.child_page = SimplePage(
title="Hello world!",
slug='hello-world',
content="hello",
live=False,
)
self.root_page.add_child(instance=self.child_page)
# POST data to edit the page
self.post_data = {
'title': "I've been edited!",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
def submit(self):
return self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), self.post_data)
def silent_submit(self):
"""
Sets up the child_page as needing moderation, without making a request
"""
self.child_page.save_revision(user=self.submitter, submitted_for_moderation=True)
self.revision = self.child_page.get_latest_revision()
def approve(self):
return self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(self.revision.id, )))
def reject(self):
return self.client.post(reverse('wagtailadmin_pages:reject_moderation', args=(self.revision.id, )))
def test_vanilla_profile(self):
# Check that the vanilla profile has rejected notifications on
self.assertEqual(self.submitter_profile.rejected_notifications, True)
# Check that the vanilla profile has approved notifications on
self.assertEqual(self.submitter_profile.approved_notifications, True)
def test_submit_notifications_sent(self):
# Submit
self.submit()
# Check that both the moderators got an email, and no others
self.assertEqual(len(mail.outbox), 2)
email_to = mail.outbox[0].to + mail.outbox[1].to
self.assertIn(self.moderator.email, email_to)
self.assertIn(self.moderator2.email, email_to)
self.assertEqual(len(mail.outbox[0].to), 1)
self.assertEqual(len(mail.outbox[1].to), 1)
def test_submit_notification_preferences_respected(self):
# moderator2 doesn't want emails
self.moderator2_profile.submitted_notifications = False
self.moderator2_profile.save()
# Submit
self.submit()
# Check that only one moderator got an email
self.assertEqual(len(mail.outbox), 1)
self.assertEqual([self.moderator.email], mail.outbox[0].to)
def test_approved_notifications(self):
# Set up the page version
self.silent_submit()
# Approve
self.approve()
# Submitter must receive 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_approved_notifications_preferences_respected(self):
# Submitter doesn't want 'approved' emails
self.submitter_profile.approved_notifications = False
self.submitter_profile.save()
# Set up the page version
self.silent_submit()
# Approve
self.approve()
# No email to send
self.assertEqual(len(mail.outbox), 0)
def test_rejected_notifications(self):
# Set up the page version
self.silent_submit()
# Reject
self.reject()
# Submitter must receive 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_rejected_notification_preferences_respected(self):
# Submitter doesn't want 'rejected' emails
self.submitter_profile.rejected_notifications = False
self.submitter_profile.save()
# Set up the page version
self.silent_submit()
# Reject
self.reject()
# No email to send
self.assertEqual(len(mail.outbox), 0)
def test_moderator_group_notifications(self):
# Create a (non-superuser) moderator
User = get_user_model()
user1 = User.objects.create_user('moduser1', 'moduser1@email.com')
user1.groups.add(Group.objects.get(name='Moderators'))
user1.save()
# Create another group and user with permission to moderate
modgroup2 = Group.objects.create(name='More moderators')
GroupPagePermission.objects.create(
group=modgroup2, page=self.root_page, permission_type='publish'
)
user2 = User.objects.create_user('moduser2', 'moduser2@email.com')
user2.groups.add(Group.objects.get(name='More moderators'))
user2.save()
# Submit
# This used to break in Wagtail 1.3 (Postgres exception, SQLite 3/4 notifications)
response = self.submit()
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the superusers and the moderation group members all got an email
expected_emails = 4
self.assertEqual(len(mail.outbox), expected_emails)
email_to = []
for i in range(expected_emails):
self.assertEqual(len(mail.outbox[i].to), 1)
email_to += mail.outbox[i].to
self.assertIn(self.moderator.email, email_to)
self.assertIn(self.moderator2.email, email_to)
self.assertIn(user1.email, email_to)
self.assertIn(user2.email, email_to)
@override_settings(WAGTAILADMIN_NOTIFICATION_INCLUDE_SUPERUSERS=False)
def test_disable_superuser_notification(self):
# Add one of the superusers to the moderator group
self.moderator.groups.add(Group.objects.get(name='Moderators'))
response = self.submit()
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the non-moderator superuser is not being notified
expected_emails = 1
self.assertEqual(len(mail.outbox), expected_emails)
# Use chain as the 'to' field is a list of recipients
email_to = list(chain.from_iterable([m.to for m in mail.outbox]))
self.assertIn(self.moderator.email, email_to)
self.assertNotIn(self.moderator2.email, email_to)
@mock.patch.object(EmailMultiAlternatives, 'send', side_effect=IOError('Server down'))
def test_email_send_error(self, mock_fn):
logging.disable(logging.CRITICAL)
# Approve
self.silent_submit()
response = self.approve()
logging.disable(logging.NOTSET)
# An email that fails to send should return a message rather than crash the page
self.assertEqual(response.status_code, 302)
response = self.client.get(reverse('wagtailadmin_home'))
# There should be one "approved" message and one "failed to send notifications"
messages = list(response.context['messages'])
self.assertEqual(len(messages), 2)
self.assertEqual(messages[0].level, message_constants.SUCCESS)
self.assertEqual(messages[1].level, message_constants.ERROR)
def test_email_headers(self):
# Submit
self.submit()
msg_headers = set(mail.outbox[0].message().items())
headers = {('Auto-Submitted', 'auto-generated')}
self.assertTrue(headers.issubset(msg_headers), msg='Message is missing the Auto-Submitted header.',)

View file

@ -0,0 +1,130 @@
from django.contrib.auth.models import Permission
from django.contrib.messages import constants as message_constants
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from wagtail.core.models import Page
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils
class TestPageMove(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Create three sections
self.section_a = SimplePage(title="Section A", slug="section-a", content="hello")
self.root_page.add_child(instance=self.section_a)
self.section_b = SimplePage(title="Section B", slug="section-b", content="hello")
self.root_page.add_child(instance=self.section_b)
self.section_c = SimplePage(title="Section C", slug="section-c", content="hello")
self.root_page.add_child(instance=self.section_c)
# Add test page A into section A
self.test_page_a = SimplePage(title="Hello world!", slug="hello-world", content="hello")
self.section_a.add_child(instance=self.test_page_a)
# Add test page B into section C
self.test_page_b = SimplePage(title="Hello world!", slug="hello-world", content="hello")
self.section_c.add_child(instance=self.test_page_b)
# Login
self.user = self.login()
def test_page_move(self):
response = self.client.get(reverse('wagtailadmin_pages:move', args=(self.test_page_a.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_a.id, )))
# Check that the user received 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_a.id, self.section_b.id))
)
self.assertEqual(response.status_code, 200)
response = self.client.get(
reverse('wagtailadmin_pages:move_confirm', args=(self.test_page_b.id, self.section_a.id))
)
# Duplicate slugs triggers a redirect with an error message.
self.assertEqual(response.status_code, 302)
response = self.client.get(reverse('wagtailadmin_home'))
messages = list(response.context['messages'])
self.assertEqual(len(messages), 1)
self.assertEqual(messages[0].level, message_constants.ERROR)
# Slug should be in error message.
self.assertIn("{}".format(self.test_page_b.slug), messages[0].message)
def test_page_set_page_position(self):
response = self.client.get(reverse('wagtailadmin_pages:set_page_position', args=(self.test_page_a.id, )))
self.assertEqual(response.status_code, 200)
def test_before_move_page_hook(self):
def hook_func(request, page, destination):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
self.assertIsInstance(destination.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('before_move_page', hook_func):
response = self.client.get(reverse('wagtailadmin_pages:move_confirm', args=(self.test_page_a.id, self.section_b.id)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_move_page_hook_post(self):
def hook_func(request, page, destination):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
self.assertIsInstance(destination.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('before_move_page', hook_func):
response = self.client.post(reverse('wagtailadmin_pages:move_confirm', args=(self.test_page_a.id, self.section_b.id)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should not be moved
self.assertEqual(
Page.objects.get(id=self.test_page_a.id).get_parent().id,
self.section_a.id
)
def test_after_move_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook('after_move_page', hook_func):
response = self.client.post(reverse('wagtailadmin_pages:move_confirm', args=(self.test_page_a.id, self.section_b.id)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should be moved
self.assertEqual(
Page.objects.get(id=self.test_page_a.id).get_parent().id,
self.section_b.id
)

View file

@ -0,0 +1,201 @@
from django.contrib.auth.models import Permission
from django.test import TestCase
from django.urls import reverse
from wagtail.core.models import Page
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils
class TestLocking(TestCase, WagtailTestUtils):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Login
self.user = self.login()
# Create a page and submit it for moderation
self.child_page = SimplePage(
title="Hello world!",
slug='hello-world',
content="hello",
live=False,
)
self.root_page.add_child(instance=self.child_page)
def test_lock_post(self):
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page is locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_get(self):
response = self.client.get(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 405)
# Check that the page is still unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_already_locked(self):
# Lock the page
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page is still locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_with_good_redirect(self):
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )), {
'next': reverse('wagtailadmin_pages:edit', args=(self.child_page.id, ))
})
# Check response
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )))
# Check that the page is locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_with_bad_redirect(self):
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )), {
'next': 'http://www.google.co.uk'
})
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page is locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_bad_page(self):
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(9999, )))
# Check response
self.assertEqual(response.status_code, 404)
# Check that the page is still unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_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()
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 403)
# Check that the page is still unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_post(self):
# Lock the page
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page is unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_get(self):
# Lock the page
self.child_page.locked = True
self.child_page.save()
response = self.client.get(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 405)
# Check that the page is still locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_post_already_unlocked(self):
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page is still unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_post_with_good_redirect(self):
# Lock the page
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )), {
'next': reverse('wagtailadmin_pages:edit', args=(self.child_page.id, ))
})
# Check response
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )))
# Check that the page is unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_post_with_bad_redirect(self):
# Lock the page
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )), {
'next': 'http://www.google.co.uk'
})
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page is unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_post_bad_page(self):
# Lock the page
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(9999, )))
# Check response
self.assertEqual(response.status_code, 404)
# Check that the page is still locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_post_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()
# Lock the page
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 403)
# Check that the page is still locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)

View file

@ -0,0 +1,177 @@
from django.contrib.auth.models import Permission
from django.test import TestCase
from django.urls import reverse
from wagtail.admin.tests.pages.timestamps import local_datetime
from wagtail.core.models import Page
from wagtail.search.index import SearchField
from wagtail.tests.testapp.models import SimplePage, SingleEventPage
from wagtail.tests.utils import WagtailTestUtils
class TestPageSearch(TestCase, WagtailTestUtils):
def setUp(self):
self.user = self.login()
def get(self, params=None, **extra):
return self.client.get(reverse('wagtailadmin_pages:search'), params or {}, **extra)
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_search_searchable_fields(self):
# Find root page
root_page = Page.objects.get(id=2)
# Create a page
root_page.add_child(instance=SimplePage(
title="Hi there!", slug='hello-world', content="good morning",
live=True,
has_unpublished_changes=False,
))
# Confirm the slug is not being searched
response = self.get({'q': "hello"})
self.assertNotContains(response, "There is one matching page")
search_fields = Page.search_fields
# Add slug to the search_fields
Page.search_fields = Page.search_fields + [SearchField('slug', partial_match=True)]
# Confirm the slug is being searched
response = self.get({'q': "hello"})
self.assertContains(response, "There is one matching page")
# Reset the search fields
Page.search_fields = search_fields
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({'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.get({'q': "roo"})
self.assertEqual(response.status_code, 200)
# 'pages' list in the response should contain root
results = response.context['pages']
self.assertTrue(any([r.slug == 'root' for r in results]))
def test_search_uses_admin_display_title_from_specific_class(self):
# SingleEventPage has a custom get_admin_display_title method; explorer should
# show the custom title rather than the basic database one
root_page = Page.objects.get(id=2)
new_event = SingleEventPage(
title="Lunar event",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1)
)
root_page.add_child(instance=new_event)
response = self.get({'q': "lunar"})
self.assertContains(response, "Lunar event (single event)")
def test_search_no_perms(self):
self.user.is_superuser = False
self.user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
self.user.save()
self.assertRedirects(self.get(), '/admin/')
def test_search_order_by_title(self):
root_page = Page.objects.get(id=2)
new_event = SingleEventPage(
title="Lunar event",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1)
)
root_page.add_child(instance=new_event)
new_event_2 = SingleEventPage(
title="A Lunar event",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1)
)
root_page.add_child(instance=new_event_2)
response = self.get({'q': 'Lunar', 'ordering': 'title'})
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [new_event_2.id, new_event.id])
response = self.get({'q': 'Lunar', 'ordering': '-title'})
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [new_event.id, new_event_2.id])
def test_search_order_by_updated(self):
root_page = Page.objects.get(id=2)
new_event = SingleEventPage(
title="Lunar event",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1)
)
root_page.add_child(instance=new_event)
new_event_2 = SingleEventPage(
title="Lunar event 2",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2015, 1, 1)
)
root_page.add_child(instance=new_event_2)
response = self.get({'q': 'Lunar', 'ordering': 'latest_revision_created_at'})
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [new_event_2.id, new_event.id])
response = self.get({'q': 'Lunar', 'ordering': '-latest_revision_created_at'})
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [new_event.id, new_event_2.id])
def test_search_order_by_status(self):
root_page = Page.objects.get(id=2)
live_event = SingleEventPage(
title="Lunar event",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1),
live=True
)
root_page.add_child(instance=live_event)
draft_event = SingleEventPage(
title="Lunar event",
location='the moon', audience='public',
cost='free', date_from='2001-01-01',
latest_revision_created_at=local_datetime(2016, 1, 1),
live=False
)
root_page.add_child(instance=draft_event)
response = self.get({'q': 'Lunar', 'ordering': 'live'})
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [draft_event.id, live_event.id])
response = self.get({'q': 'Lunar', 'ordering': '-live'})
page_ids = [page.id for page in response.context['pages']]
self.assertEqual(page_ids, [live_event.id, draft_event.id])

View file

@ -0,0 +1,163 @@
import datetime
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from freezegun import freeze_time
from wagtail.admin.views.pages import PreviewOnEdit
from wagtail.core.models import Page
from wagtail.tests.testapp.models import EventCategory
from wagtail.tests.utils import WagtailTestUtils
class TestIssue2599(TestCase, WagtailTestUtils):
"""
When previewing a page on creation, we need to assign it a path value consistent with its
(future) position in the tree. The naive way of doing this is to give it an index number
one more than numchild - however, index numbers are not reassigned on page deletion, so
this can result in a path that collides with an existing page (which is invalid).
"""
def test_issue_2599(self):
homepage = Page.objects.get(id=2)
child1 = Page(title='child1')
homepage.add_child(instance=child1)
child2 = Page(title='child2')
homepage.add_child(instance=child2)
child1.delete()
self.login()
post_data = {
'title': "New page!",
'content': "Some content",
'slug': 'hello-world',
'action-submit': "Submit",
}
preview_url = reverse('wagtailadmin_pages:preview_on_add',
args=('tests', 'simplepage', homepage.id))
response = self.client.post(preview_url, post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content.decode(), {'is_valid': True})
response = self.client.get(preview_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'tests/simple_page.html')
self.assertContains(response, "New page!")
# Check that the treebeard attributes were set correctly on the page object
self.assertEqual(response.context['self'].depth, homepage.depth + 1)
self.assertTrue(response.context['self'].path.startswith(homepage.path))
self.assertEqual(response.context['self'].get_parent(), homepage)
class TestPreview(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def setUp(self):
self.meetings_category = EventCategory.objects.create(name='Meetings')
self.parties_category = EventCategory.objects.create(name='Parties')
self.holidays_category = EventCategory.objects.create(name='Holidays')
self.home_page = Page.objects.get(url_path='/home/')
self.event_page = Page.objects.get(url_path='/home/events/christmas/')
self.user = self.login()
self.post_data = {
'title': "Beach party",
'slug': 'beach-party',
'body': '''{"entityMap": {},"blocks": [
{"inlineStyleRanges": [], "text": "party on wayne", "depth": 0, "type": "unstyled", "key": "00000", "entityRanges": []}
]}''',
'date_from': '2017-08-01',
'audience': 'public',
'location': 'the beach',
'cost': 'six squid',
'carousel_items-TOTAL_FORMS': 0,
'carousel_items-INITIAL_FORMS': 0,
'carousel_items-MIN_NUM_FORMS': 0,
'carousel_items-MAX_NUM_FORMS': 0,
'speakers-TOTAL_FORMS': 0,
'speakers-INITIAL_FORMS': 0,
'speakers-MIN_NUM_FORMS': 0,
'speakers-MAX_NUM_FORMS': 0,
'related_links-TOTAL_FORMS': 0,
'related_links-INITIAL_FORMS': 0,
'related_links-MIN_NUM_FORMS': 0,
'related_links-MAX_NUM_FORMS': 0,
'head_counts-TOTAL_FORMS': 0,
'head_counts-INITIAL_FORMS': 0,
'head_counts-MIN_NUM_FORMS': 0,
'head_counts-MAX_NUM_FORMS': 0,
'categories': [self.parties_category.id, self.holidays_category.id],
}
def test_preview_on_create_with_m2m_field(self):
preview_url = reverse('wagtailadmin_pages:preview_on_add',
args=('tests', 'eventpage', self.home_page.id))
response = self.client.post(preview_url, self.post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content.decode(), {'is_valid': True})
response = self.client.get(preview_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'tests/event_page.html')
self.assertContains(response, "Beach party")
self.assertContains(response, "<li>Parties</li>")
self.assertContains(response, "<li>Holidays</li>")
def test_preview_on_edit_with_m2m_field(self):
preview_url = reverse('wagtailadmin_pages:preview_on_edit',
args=(self.event_page.id,))
response = self.client.post(preview_url, self.post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content.decode(), {'is_valid': True})
response = self.client.get(preview_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'tests/event_page.html')
self.assertContains(response, "Beach party")
self.assertContains(response, "<li>Parties</li>")
self.assertContains(response, "<li>Holidays</li>")
def test_preview_on_edit_expiry(self):
initial_datetime = timezone.now()
expiry_datetime = initial_datetime + datetime.timedelta(
seconds=PreviewOnEdit.preview_expiration_timeout + 1)
with freeze_time(initial_datetime) as frozen_datetime:
preview_url = reverse('wagtailadmin_pages:preview_on_edit',
args=(self.event_page.id,))
response = self.client.post(preview_url, self.post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
response = self.client.get(preview_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
frozen_datetime.move_to(expiry_datetime)
preview_url = reverse('wagtailadmin_pages:preview_on_edit',
args=(self.home_page.id,))
response = self.client.post(preview_url, self.post_data)
self.assertEqual(response.status_code, 200)
response = self.client.get(preview_url)
self.assertEqual(response.status_code, 200)

View file

@ -0,0 +1,460 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.test import TestCase
from django.urls import reverse
from django.utils import formats
from django.utils.dateparse import parse_date
from wagtail.admin.tests.pages.timestamps import local_datetime
from wagtail.core.models import Page
from wagtail.tests.testapp.models import EventPage, FormClassAdditionalFieldPage
from wagtail.tests.utils import WagtailTestUtils
class TestRevisions(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def setUp(self):
self.christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
self.christmas_event.title = "Last Christmas"
self.christmas_event.date_from = '2013-12-25'
self.christmas_event.body = (
"<p>Last Christmas I gave you my heart, "
"but the very next day you gave it away</p>"
)
self.last_christmas_revision = self.christmas_event.save_revision()
self.last_christmas_revision.created_at = local_datetime(2013, 12, 25)
self.last_christmas_revision.save()
self.christmas_event.title = "This Christmas"
self.christmas_event.date_from = '2014-12-25'
self.christmas_event.body = (
"<p>This year, to save me from tears, "
"I'll give it to someone special</p>"
)
self.this_christmas_revision = self.christmas_event.save_revision()
self.this_christmas_revision.created_at = local_datetime(2014, 12, 25)
self.this_christmas_revision.save()
self.login()
def test_edit_form_has_revisions_link(self):
response = self.client.get(
reverse('wagtailadmin_pages:edit', args=(self.christmas_event.id, ))
)
self.assertEqual(response.status_code, 200)
revisions_index_url = reverse(
'wagtailadmin_pages:revisions_index', args=(self.christmas_event.id, )
)
self.assertContains(response, revisions_index_url)
def test_get_revisions_index(self):
response = self.client.get(
reverse('wagtailadmin_pages:revisions_index', args=(self.christmas_event.id, ))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, formats.localize(parse_date('2013-12-25')))
last_christmas_preview_url = reverse(
'wagtailadmin_pages:revisions_view',
args=(self.christmas_event.id, self.last_christmas_revision.id)
)
last_christmas_revert_url = reverse(
'wagtailadmin_pages:revisions_revert',
args=(self.christmas_event.id, self.last_christmas_revision.id)
)
self.assertContains(response, last_christmas_preview_url)
self.assertContains(response, last_christmas_revert_url)
self.assertContains(response, formats.localize(local_datetime(2014, 12, 25)))
this_christmas_preview_url = reverse(
'wagtailadmin_pages:revisions_view',
args=(self.christmas_event.id, self.this_christmas_revision.id)
)
this_christmas_revert_url = reverse(
'wagtailadmin_pages:revisions_revert',
args=(self.christmas_event.id, self.this_christmas_revision.id)
)
self.assertContains(response, this_christmas_preview_url)
self.assertContains(response, this_christmas_revert_url)
def request_preview_revision(self):
last_christmas_preview_url = reverse(
'wagtailadmin_pages:revisions_view',
args=(self.christmas_event.id, self.last_christmas_revision.id)
)
return self.client.get(last_christmas_preview_url)
def test_preview_revision(self):
response = self.request_preview_revision()
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Last Christmas I gave you my heart")
def test_preview_revision_with_no_page_permissions_redirects_to_admin(self):
admin_only_user = get_user_model().objects.create_user(
username='admin_only',
email='admin_only@email.com',
password='password'
)
admin_only_user.user_permissions.add(
Permission.objects.get_by_natural_key(
codename='access_admin',
app_label='wagtailadmin',
model='admin'
)
)
self.login(user=admin_only_user)
response = self.request_preview_revision()
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], reverse('wagtailadmin_home'))
def test_preview_revision_forbidden_without_permission(self):
# Alter the editors group so it has no permissions for Christmas page.
st_patricks = Page.objects.get(slug='saint-patrick')
editors_group = Group.objects.get(name='Site-wide editors')
editors_group.page_permissions.update(page_id=st_patricks.id)
editor = get_user_model().objects.get(username='siteeditor')
self.login(editor)
response = self.request_preview_revision()
self.assertEqual(response.status_code, 403)
def test_revert_revision(self):
last_christmas_preview_url = reverse(
'wagtailadmin_pages:revisions_revert',
args=(self.christmas_event.id, self.last_christmas_revision.id)
)
response = self.client.get(last_christmas_preview_url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Editing Event page")
self.assertContains(response, "You are viewing a previous revision of this page")
# Form should show the content of the revision, not the current draft
self.assertContains(response, "Last Christmas I gave you my heart")
# Form should include a hidden 'revision' field
revision_field = (
"""<input type="hidden" name="revision" value="%d" />""" %
self.last_christmas_revision.id
)
self.assertContains(response, revision_field)
# Buttons should be relabelled
self.assertContains(response, "Replace current draft")
self.assertContains(response, "Publish this revision")
def test_scheduled_revision(self):
self.last_christmas_revision.publish()
self.this_christmas_revision.approved_go_live_at = local_datetime(2014, 12, 26)
self.this_christmas_revision.save()
this_christmas_unschedule_url = reverse(
'wagtailadmin_pages:revisions_unschedule',
args=(self.christmas_event.id, self.this_christmas_revision.id)
)
response = self.client.get(
reverse('wagtailadmin_pages:revisions_index', args=(self.christmas_event.id, ))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Scheduled for')
self.assertContains(response, formats.localize(parse_date('2014-12-26')))
self.assertContains(response, this_christmas_unschedule_url)
class TestCompareRevisions(TestCase, WagtailTestUtils):
# Actual tests for the comparison classes can be found in test_compare.py
fixtures = ['test.json']
def setUp(self):
self.christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
self.christmas_event.title = "Last Christmas"
self.christmas_event.date_from = '2013-12-25'
self.christmas_event.body = (
"<p>Last Christmas I gave you my heart, "
"but the very next day you gave it away</p>"
)
self.last_christmas_revision = self.christmas_event.save_revision()
self.last_christmas_revision.created_at = local_datetime(2013, 12, 25)
self.last_christmas_revision.save()
self.christmas_event.title = "This Christmas"
self.christmas_event.date_from = '2014-12-25'
self.christmas_event.body = (
"<p>This year, to save me from tears, "
"I'll give it to someone special</p>"
)
self.this_christmas_revision = self.christmas_event.save_revision()
self.this_christmas_revision.created_at = local_datetime(2014, 12, 25)
self.this_christmas_revision.save()
self.login()
def test_compare_revisions(self):
compare_url = reverse(
'wagtailadmin_pages:revisions_compare',
args=(self.christmas_event.id, self.last_christmas_revision.id, self.this_christmas_revision.id)
)
response = self.client.get(compare_url)
self.assertEqual(response.status_code, 200)
self.assertContains(
response,
'<span class="deletion">Last Christmas I gave you my heart, but the very next day you gave it away</span><span class="addition">This year, to save me from tears, I&#39;ll give it to someone special</span>',
html=True
)
def test_compare_revisions_earliest(self):
compare_url = reverse(
'wagtailadmin_pages:revisions_compare',
args=(self.christmas_event.id, 'earliest', self.this_christmas_revision.id)
)
response = self.client.get(compare_url)
self.assertEqual(response.status_code, 200)
self.assertContains(
response,
'<span class="deletion">Last Christmas I gave you my heart, but the very next day you gave it away</span><span class="addition">This year, to save me from tears, I&#39;ll give it to someone special</span>',
html=True
)
def test_compare_revisions_latest(self):
compare_url = reverse(
'wagtailadmin_pages:revisions_compare',
args=(self.christmas_event.id, self.last_christmas_revision.id, 'latest')
)
response = self.client.get(compare_url)
self.assertEqual(response.status_code, 200)
self.assertContains(
response,
'<span class="deletion">Last Christmas I gave you my heart, but the very next day you gave it away</span><span class="addition">This year, to save me from tears, I&#39;ll give it to someone special</span>',
html=True
)
def test_compare_revisions_live(self):
# Mess with the live version, bypassing revisions
self.christmas_event.body = (
"<p>This year, to save me from tears, "
"I'll just feed it to the dog</p>"
)
self.christmas_event.save(update_fields=['body'])
compare_url = reverse(
'wagtailadmin_pages:revisions_compare',
args=(self.christmas_event.id, self.last_christmas_revision.id, 'live')
)
response = self.client.get(compare_url)
self.assertEqual(response.status_code, 200)
self.assertContains(
response,
'<span class="deletion">Last Christmas I gave you my heart, but the very next day you gave it away</span><span class="addition">This year, to save me from tears, I&#39;ll just feed it to the dog</span>',
html=True
)
class TestCompareRevisionsWithNonModelField(TestCase, WagtailTestUtils):
"""
Tests if form fields defined in the base_form_class will not be included.
in revisions view as they are not actually on the model.
Flagged in issue #3737
Note: Actual tests for comparison classes can be found in test_compare.py
"""
fixtures = ['test.json']
# FormClassAdditionalFieldPage
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child page of class with base_form_class override
# non model field is 'code'
self.test_page = FormClassAdditionalFieldPage(
title='A Statement',
slug='a-statement',
location='Early Morning Cafe, Mainland, NZ',
body="<p>hello</p>"
)
self.root_page.add_child(instance=self.test_page)
# add new revision
self.test_page.title = 'Statement'
self.test_page.location = 'Victory Monument, Bangkok'
self.test_page.body = (
"<p>I would like very much to go into the forrest.</p>"
)
self.test_page_revision = self.test_page.save_revision()
self.test_page_revision.created_at = local_datetime(2017, 10, 15)
self.test_page_revision.save()
# add another new revision
self.test_page.title = 'True Statement'
self.test_page.location = 'Victory Monument, Bangkok'
self.test_page.body = (
"<p>I would like very much to go into the forest.</p>"
)
self.test_page_revision_new = self.test_page.save_revision()
self.test_page_revision_new.created_at = local_datetime(2017, 10, 16)
self.test_page_revision_new.save()
self.login()
def test_base_form_class_used(self):
"""First ensure that the non-model field is appearing in edit."""
edit_url = reverse('wagtailadmin_pages:add', args=('tests', 'formclassadditionalfieldpage', self.test_page.id))
response = self.client.get(edit_url)
self.assertContains(response, '<input type="text" name="code" required id="id_code" maxlength="5" />', html=True)
def test_compare_revisions(self):
"""Confirm that the non-model field is not shown in revision."""
compare_url = reverse(
'wagtailadmin_pages:revisions_compare',
args=(self.test_page.id, self.test_page_revision.id, self.test_page_revision_new.id)
)
response = self.client.get(compare_url)
self.assertContains(response, '<span class="deletion">forrest.</span><span class="addition">forest.</span>')
# should not contain the field defined in the formclass used
self.assertNotContains(response, '<h2>Code:</h2>')
class TestRevisionsUnschedule(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def setUp(self):
self.christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
self.christmas_event.title = "Last Christmas"
self.christmas_event.date_from = '2013-12-25'
self.christmas_event.body = (
"<p>Last Christmas I gave you my heart, "
"but the very next day you gave it away</p>"
)
self.last_christmas_revision = self.christmas_event.save_revision()
self.last_christmas_revision.created_at = local_datetime(2013, 12, 25)
self.last_christmas_revision.save()
self.last_christmas_revision.publish()
self.christmas_event.title = "This Christmas"
self.christmas_event.date_from = '2014-12-25'
self.christmas_event.body = (
"<p>This year, to save me from tears, "
"I'll give it to someone special</p>"
)
self.this_christmas_revision = self.christmas_event.save_revision()
self.this_christmas_revision.created_at = local_datetime(2014, 12, 24)
self.this_christmas_revision.save()
self.this_christmas_revision.approved_go_live_at = local_datetime(2014, 12, 25)
self.this_christmas_revision.save()
self.user = self.login()
def test_unschedule_view(self):
"""
This tests that the unschedule view responds with a confirm page
"""
response = self.client.get(reverse('wagtailadmin_pages:revisions_unschedule', args=(self.christmas_event.id, self.this_christmas_revision.id)))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/revisions/confirm_unschedule.html')
def test_unschedule_view_invalid_page_id(self):
"""
This tests that the unschedule view returns an error if the page id is invalid
"""
# Get unschedule page
response = self.client.get(reverse('wagtailadmin_pages:revisions_unschedule', args=(12345, 67894)))
# Check that the user received a 404 response
self.assertEqual(response.status_code, 404)
def test_unschedule_view_invalid_revision_id(self):
"""
This tests that the unschedule view returns an error if the page id is invalid
"""
# Get unschedule page
response = self.client.get(reverse('wagtailadmin_pages:revisions_unschedule', args=(self.christmas_event.id, 67894)))
# Check that the user received a 404 response
self.assertEqual(response.status_code, 404)
def test_unschedule_view_bad_permissions(self):
"""
This tests that the unschedule view doesn't allow users without publish 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 unschedule page
response = self.client.get(reverse('wagtailadmin_pages:revisions_unschedule', args=(self.christmas_event.id, self.this_christmas_revision.id)))
# Check that the user received a 403 response
self.assertEqual(response.status_code, 403)
def test_unschedule_view_post(self):
"""
This posts to the unschedule view and checks that the revision was unscheduled
"""
# Post to the unschedule page
response = self.client.post(reverse('wagtailadmin_pages:revisions_unschedule', args=(self.christmas_event.id, self.this_christmas_revision.id)))
# Should be redirected to revisions index page
self.assertRedirects(response, reverse('wagtailadmin_pages:revisions_index', args=(self.christmas_event.id, )))
# Check that the page has no approved_schedule
self.assertFalse(EventPage.objects.get(id=self.christmas_event.id).approved_schedule)
# Check that the approved_go_live_at has been cleared from the revision
self.assertIsNone(self.christmas_event.revisions.get(id=self.this_christmas_revision.id).approved_go_live_at)
class TestRevisionsUnscheduleForUnpublishedPages(TestCase, WagtailTestUtils):
fixtures = ['test.json']
def setUp(self):
self.unpublished_event = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/')
self.unpublished_event.title = "Unpublished Page"
self.unpublished_event.date_from = '2014-12-25'
self.unpublished_event.body = (
"<p>Some Content</p>"
)
self.unpublished_revision = self.unpublished_event.save_revision()
self.unpublished_revision.created_at = local_datetime(2014, 12, 25)
self.unpublished_revision.save()
self.user = self.login()
def test_unschedule_view(self):
"""
This tests that the unschedule view responds with a confirm page
"""
response = self.client.get(reverse('wagtailadmin_pages:revisions_unschedule', args=(self.unpublished_event.id, self.unpublished_revision.id)))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/revisions/confirm_unschedule.html')
def test_unschedule_view_post(self):
"""
This posts to the unschedule view and checks that the revision was unscheduled
"""
# Post to the unschedule page
response = self.client.post(reverse('wagtailadmin_pages:revisions_unschedule', args=(self.unpublished_event.id, self.unpublished_revision.id)))
# Should be redirected to revisions index page
self.assertRedirects(response, reverse('wagtailadmin_pages:revisions_index', args=(self.unpublished_event.id, )))
# Check that the page has no approved_schedule
self.assertFalse(EventPage.objects.get(id=self.unpublished_event.id).approved_schedule)
# Check that the approved_go_live_at has been cleared from the revision
self.assertIsNone(self.unpublished_event.revisions.get(id=self.unpublished_revision.id).approved_go_live_at)

View file

@ -0,0 +1,181 @@
from unittest import mock
from django.contrib.auth.models import Permission
from django.test import TestCase
from django.urls import reverse
from wagtail.core.models import Page
from wagtail.core.signals import page_unpublished
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils
class TestPageUnpublish(TestCase, WagtailTestUtils):
def setUp(self):
self.user = self.login()
# Create a page to unpublish
self.root_page = Page.objects.get(id=2)
self.page = SimplePage(
title="Hello world!",
slug='hello-world',
content="hello",
live=True,
)
self.root_page.add_child(instance=self.page)
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 received an unpublish confirm page
self.assertEqual(response.status_code, 200)
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 received 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 received 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
"""
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
# Post to the unpublish page
response = self.client.post(reverse('wagtailadmin_pages:unpublish', args=(self.page.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page was unpublished
self.assertFalse(SimplePage.objects.get(id=self.page.id).live)
# Check that the page_unpublished signal was fired
self.assertEqual(mock_handler.call_count, 1)
mock_call = mock_handler.mock_calls[0][2]
self.assertEqual(mock_call['sender'], self.page.specific_class)
self.assertEqual(mock_call['instance'], self.page)
self.assertIsInstance(mock_call['instance'], self.page.specific_class)
def test_unpublish_descendants_view(self):
"""
This tests that the unpublish view responds with an unpublish confirm page that does not contain the form field 'include_descendants'
"""
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages:unpublish', args=(self.page.id, )))
# Check that the user received an unpublish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/confirm_unpublish.html')
# Check the form does not contain the checkbox field include_descendants
self.assertNotContains(response, '<input id="id_include_descendants" name="include_descendants" type="checkbox">')
class TestPageUnpublishIncludingDescendants(TestCase, WagtailTestUtils):
def setUp(self):
self.user = self.login()
# Find root page
self.root_page = Page.objects.get(id=2)
# Create a page to unpublish
self.test_page = self.root_page.add_child(instance=SimplePage(
title="Hello world!",
slug='hello-world',
content="hello",
live=True,
has_unpublished_changes=False,
))
# Create a couple of child pages
self.test_child_page = self.test_page.add_child(instance=SimplePage(
title="Child page",
slug='child-page',
content="hello",
live=True,
has_unpublished_changes=True,
))
self.test_another_child_page = self.test_page.add_child(instance=SimplePage(
title="Another Child page",
slug='another-child-page',
content="hello",
live=True,
has_unpublished_changes=True,
))
def test_unpublish_descendants_view(self):
"""
This tests that the unpublish view responds with an unpublish confirm page that contains the form field 'include_descendants'
"""
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages:unpublish', args=(self.test_page.id, )))
# Check that the user received an unpublish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/confirm_unpublish.html')
# Check the form contains the checkbox field include_descendants
self.assertContains(response, '<input id="id_include_descendants" name="include_descendants" type="checkbox">')
def test_unpublish_include_children_view_post(self):
"""
This posts to the unpublish view and checks that the page and its descendants were unpublished
"""
# Post to the unpublish page
response = self.client.post(reverse('wagtailadmin_pages:unpublish', args=(self.test_page.id, )), {'include_descendants': 'on'})
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page was unpublished
self.assertFalse(SimplePage.objects.get(id=self.test_page.id).live)
# Check that the descendant pages were unpiblished as well
self.assertFalse(SimplePage.objects.get(id=self.test_child_page.id).live)
self.assertFalse(SimplePage.objects.get(id=self.test_another_child_page.id).live)
def test_unpublish_not_include_children_view_post(self):
"""
This posts to the unpublish view and checks that the page was unpublished but its descendants were not
"""
# Post to the unpublish page
response = self.client.post(reverse('wagtailadmin_pages:unpublish', args=(self.test_page.id, )), {})
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
# Check that the page was unpublished
self.assertFalse(SimplePage.objects.get(id=self.test_page.id).live)
# Check that the descendant pages were not unpublished
self.assertTrue(SimplePage.objects.get(id=self.test_child_page.id).live)
self.assertTrue(SimplePage.objects.get(id=self.test_another_child_page.id).live)

View file

@ -0,0 +1,78 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.test import TestCase
from django.urls import reverse
from wagtail.core.models import Page
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils
class TestDraftAccess(TestCase, WagtailTestUtils):
"""Tests for the draft view access restrictions."""
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child page
self.child_page = SimplePage(
title="Hello world!",
slug="hello-world",
content="hello",
)
self.root_page.add_child(instance=self.child_page)
# create user with admin access (but not draft_view access)
user = get_user_model().objects.create_user(username='bob', email='bob@email.com', password='password')
user.user_permissions.add(
Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
)
def test_draft_access_admin(self):
"""Test that admin can view draft."""
# Login as admin
self.user = self.login()
# Try getting page draft
response = self.client.get(reverse('wagtailadmin_pages:view_draft', args=(self.child_page.id, )))
# User can view
self.assertEqual(response.status_code, 200)
def test_draft_access_unauthorized(self):
"""Test that user without edit/publish permission can't view draft."""
self.assertTrue(self.client.login(username='bob', password='password'))
# Try getting page draft
response = self.client.get(reverse('wagtailadmin_pages:view_draft', args=(self.child_page.id, )))
# User gets Unauthorized response
self.assertEqual(response.status_code, 403)
def test_draft_access_authorized(self):
"""Test that user with edit permission can view draft."""
# give user the permission to edit page
user = get_user_model().objects.get(username='bob')
user.groups.add(Group.objects.get(name='Moderators'))
user.save()
self.assertTrue(self.client.login(username='bob', password='password'))
# Get add subpage page
response = self.client.get(reverse('wagtailadmin_pages:view_draft', args=(self.child_page.id, )))
# User can view
self.assertEqual(response.status_code, 200)
def test_middleware_response_is_returned(self):
"""
If middleware returns a response while serving a page preview, that response should be
returned back to the user
"""
self.login()
response = self.client.get(
reverse('wagtailadmin_pages:view_draft', args=(self.child_page.id, )),
HTTP_USER_AGENT='EvilHacker'
)
self.assertEqual(response.status_code, 403)

View file

@ -0,0 +1,19 @@
import datetime
from django.utils import timezone
def submittable_timestamp(timestamp):
"""
Helper function to translate a possibly-timezone-aware datetime into the format used in the
go_live_at / expire_at form fields - "YYYY-MM-DD hh:mm", with no timezone indicator.
This will be interpreted as being in the server's timezone (settings.TIME_ZONE), so we
need to pass it through timezone.localtime to ensure that the client and server are in
agreement about what the timestamp means.
"""
return timezone.localtime(timestamp).strftime("%Y-%m-%d %H:%M")
def local_datetime(*args):
dt = datetime.datetime(*args)
return timezone.make_aware(dt)

File diff suppressed because it is too large Load diff