Initial commit

This commit is contained in:
2024-08-27 20:33:44 +02:00
commit 1f1832267d
14794 changed files with 1599592 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
from wagtail.coreutils import get_dummy_request
from wagtail.models import Page, Site
from wagtail.test.testapp.models import TestGenericSetting
class GenericSettingsTestMixin:
def setUp(self):
root = Page.objects.first()
other_root = Page(title="Other Root")
root.add_child(instance=other_root)
self.default_site = Site.objects.get(is_default_site=True)
self.other_site = Site.objects.create(hostname="other", root_page=other_root)
self.default_settings = TestGenericSetting.objects.create(
title="Default GenericSettings title", email="email@example.com"
)
def get_request(self, site=None):
if site is None:
site = self.default_site
return get_dummy_request(site=site)

View File

@@ -0,0 +1,421 @@
from django.contrib.auth.models import Permission
from django.test import TestCase
from django.urls import reverse
from django.utils.text import capfirst
from wagtail import hooks
from wagtail.admin.admin_url_finder import AdminURLFinder
from wagtail.admin.panels import FieldPanel, ObjectList, TabbedInterface
from wagtail.contrib.settings.registry import SettingMenuItem
from wagtail.contrib.settings.views import get_setting_edit_handler
from wagtail.test.testapp.models import (
FileGenericSetting,
IconGenericSetting,
PanelGenericSettings,
TabbedGenericSettings,
TestGenericSetting,
TestPermissionedGenericSetting,
)
from wagtail.test.utils import WagtailTestUtils
class TestGenericSettingMenu(WagtailTestUtils, TestCase):
def login_only_admin(self):
"""Log in with a user that only has permission to access the admin"""
user = self.create_user(username="test", password="password")
user.user_permissions.add(
Permission.objects.get_by_natural_key(
codename="access_admin", app_label="wagtailadmin", model="admin"
)
)
self.login(username="test", password="password")
return user
def test_menu_item_in_admin(self):
self.login()
response = self.client.get(reverse("wagtailadmin_home"))
self.assertContains(response, capfirst(TestGenericSetting._meta.verbose_name))
self.assertContains(
response,
reverse("wagtailsettings:edit", args=("tests", "testgenericsetting")),
)
def test_menu_item_no_permissions(self):
self.login_only_admin()
response = self.client.get(reverse("wagtailadmin_home"))
self.assertNotContains(response, TestGenericSetting._meta.verbose_name)
self.assertNotContains(
response,
reverse("wagtailsettings:edit", args=("tests", "testgenericsetting")),
)
def test_menu_item_icon(self):
menu_item = SettingMenuItem(
IconGenericSetting, icon="tag", classname="test-class"
)
self.assertEqual(menu_item.icon_name, "tag")
self.assertEqual(menu_item.classname, "test-class")
class BaseTestGenericSettingView(WagtailTestUtils, TestCase):
def get(self, params={}, setting=TestGenericSetting):
url = self.edit_url(setting=setting)
return self.client.get(url, params)
def post(self, post_data={}, setting=TestGenericSetting):
url = self.edit_url(setting=setting)
return self.client.post(url, post_data)
def edit_url(self, setting):
pk = setting._get_or_create().pk
args = [setting._meta.app_label, setting._meta.model_name, pk]
return reverse("wagtailsettings:edit", args=args)
class TestGenericSettingCreateView(BaseTestGenericSettingView):
def setUp(self):
self.user = self.login()
def test_get_edit(self):
response = self.get()
self.assertEqual(response.status_code, 200)
def test_edit_invalid(self):
response = self.post(post_data={"foo": "bar"})
self.assertContains(response, "The setting could not be saved due to errors.")
self.assertContains(response, "This field is required", count=2)
def test_edit(self):
response = self.post(
post_data={
"title": "Edited setting title",
"email": "edited.email@example.com",
}
)
self.assertEqual(response.status_code, 302)
setting = TestGenericSetting.objects.first()
self.assertEqual(setting.title, "Edited setting title")
url_finder = AdminURLFinder(self.user)
expected_url = "/admin/settings/tests/testgenericsetting/%d/" % setting.pk
self.assertEqual(url_finder.get_edit_url(setting), expected_url)
def test_file_upload_multipart(self):
response = self.get(setting=FileGenericSetting)
# Ensure the form supports file uploads
self.assertContains(response, 'enctype="multipart/form-data"')
def test_create_restricted_field_without_any_permission(self):
# User has no permissions over the setting model, only access to the admin
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
)
self.assertFalse(TestPermissionedGenericSetting.objects.exists())
# GET should redirect away with permission denied
response = self.get(setting=TestPermissionedGenericSetting)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# the GET might create a setting object, depending on when the permission check is done,
# so remove any created objects prior to testing the POST
TestPermissionedGenericSetting.objects.all().delete()
# POST should redirect away with permission denied
response = self.post(
post_data={"sensitive_email": "test@example.com", "title": "test"},
setting=TestPermissionedGenericSetting,
)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# The retrieved setting should contain none of the submitted data
setting = TestPermissionedGenericSetting.load()
self.assertEqual(setting.title, "")
self.assertEqual(setting.sensitive_email, "")
def test_create_restricted_field_without_field_permission(self):
# User has edit permission over the setting model, but not the sensitive_email field
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedgenericsetting",
),
)
self.assertFalse(TestPermissionedGenericSetting.objects.exists())
# GET should provide a form with title but not sensitive_email
response = self.get(setting=TestPermissionedGenericSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertNotIn("sensitive_email", list(response.context["form"].fields))
# the GET creates a setting object, so remove any created objects prior to testing the POST
TestPermissionedGenericSetting.objects.all().delete()
# POST should allow the title to be set, but not the sensitive_email
response = self.post(
post_data={"sensitive_email": "test@example.com", "title": "test"},
setting=TestPermissionedGenericSetting,
)
self.assertEqual(response.status_code, 302)
settings = TestPermissionedGenericSetting.objects.get()
self.assertEqual(settings.title, "test")
self.assertEqual(settings.sensitive_email, "")
def test_create_restricted_field(self):
# User has edit permission over the setting model, including the sensitive_email field
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedgenericsetting",
),
Permission.objects.get(codename="can_edit_sensitive_email_generic_setting"),
)
self.assertFalse(TestPermissionedGenericSetting.objects.exists())
# GET should provide a form with title and sensitive_email
response = self.get(setting=TestPermissionedGenericSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertIn("sensitive_email", list(response.context["form"].fields))
# the GET creates a setting object, so remove any created objects prior to testing the POST
TestPermissionedGenericSetting.objects.all().delete()
# POST should allow both title and sensitive_email to be set
self.assertFalse(TestPermissionedGenericSetting.objects.exists())
response = self.post(
post_data={"sensitive_email": "test@example.com", "title": "test"},
setting=TestPermissionedGenericSetting,
)
self.assertEqual(response.status_code, 302)
settings = TestPermissionedGenericSetting.objects.get()
self.assertEqual(settings.title, "test")
self.assertEqual(settings.sensitive_email, "test@example.com")
class TestGenericSettingEditView(BaseTestGenericSettingView):
def setUp(self):
self.test_setting = TestGenericSetting()
self.test_setting.title = "Setting title"
self.test_setting.save()
self.user = self.login()
def test_get_edit(self):
response = self.get()
self.assertEqual(response.status_code, 200)
def test_non_existent_model(self):
response = self.client.get(
reverse("wagtailsettings:edit", args=["test", "foo", 1])
)
self.assertEqual(response.status_code, 404)
def test_register_with_icon(self):
edit_url = reverse("wagtailsettings:edit", args=("tests", "IconGenericSetting"))
edit_response = self.client.get(edit_url, follow=True)
self.assertContains(
edit_response,
"""<svg class="icon icon-icon-setting-tag w-header__glyph" aria-hidden="true"><use href="#icon-icon-setting-tag"></use></svg>""",
)
def test_edit_invalid(self):
response = self.post(post_data={"foo": "bar"})
self.assertContains(response, "The setting could not be saved due to errors.")
self.assertContains(response, "This field is required", count=2)
def test_edit(self):
response = self.post(
post_data={
"title": "Edited setting title",
"email": "different.email@example.com",
}
)
self.assertEqual(response.status_code, 302)
setting = TestGenericSetting.objects.first()
self.assertEqual(setting.title, "Edited setting title")
def test_for_request(self):
url = reverse("wagtailsettings:edit", args=("tests", "testgenericsetting"))
response = self.client.get(url)
self.assertRedirects(
response,
status_code=302,
expected_url=f"{url}{TestGenericSetting.objects.first().pk}/",
)
def test_edit_restricted_field(self):
# User has edit permission over the setting model, including the sensitive_email field
test_setting = TestPermissionedGenericSetting()
test_setting.sensitive_email = "test@example.com"
test_setting.title = "Old title"
test_setting.save()
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedgenericsetting",
),
Permission.objects.get(codename="can_edit_sensitive_email_generic_setting"),
)
# GET should provide a form with title and sensitive_email
response = self.get(setting=TestPermissionedGenericSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertIn("sensitive_email", list(response.context["form"].fields))
# POST should allow both title and sensitive_email to be set
response = self.post(
setting=TestPermissionedGenericSetting,
post_data={
"sensitive_email": "test-updated@example.com",
"title": "New title",
},
)
self.assertEqual(response.status_code, 302)
test_setting.refresh_from_db()
self.assertEqual(test_setting.sensitive_email, "test-updated@example.com")
self.assertEqual(test_setting.title, "New title")
def test_edit_restricted_field_without_field_permission(self):
# User has edit permission over the setting model, but not the sensitive_email field
test_setting = TestPermissionedGenericSetting()
test_setting.sensitive_email = "test@example.com"
test_setting.title = "Old title"
test_setting.save()
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedgenericsetting",
),
)
# GET should provide a form with title but not sensitive_email
response = self.get(setting=TestPermissionedGenericSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertNotIn("sensitive_email", list(response.context["form"].fields))
# POST should allow the title to be set, but not the sensitive_email
response = self.post(
setting=TestPermissionedGenericSetting,
post_data={
"sensitive_email": "test-updated@example.com",
"title": "New title",
},
)
self.assertEqual(response.status_code, 302)
test_setting.refresh_from_db()
self.assertEqual(test_setting.sensitive_email, "test@example.com")
self.assertEqual(test_setting.title, "New title")
def test_edit_restricted_field_without_any_permission(self):
# User has no permissions over the setting model, only access to the admin
test_setting = TestPermissionedGenericSetting()
test_setting.sensitive_email = "test@example.com"
test_setting.title = "Old title"
test_setting.save()
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
)
# GET should redirect away with permission denied
response = self.get(setting=TestPermissionedGenericSetting)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# POST should redirect away with permission denied
response = self.post(
setting=TestPermissionedGenericSetting,
post_data={
"sensitive_email": "test-updated@example.com",
"title": "new title",
},
)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# The retrieved setting should be unchanged
test_setting.refresh_from_db()
self.assertEqual(test_setting.sensitive_email, "test@example.com")
self.assertEqual(test_setting.title, "Old title")
class TestAdminPermission(WagtailTestUtils, TestCase):
def test_registered_permission(self):
permission = Permission.objects.get_by_natural_key(
app_label="tests",
model="testgenericsetting",
codename="change_testgenericsetting",
)
for fn in hooks.get_hooks("register_permissions"):
if permission in fn():
break
else:
self.fail("Change permission for tests.TestGenericSetting not registered")
class TestEditHandlers(TestCase):
def setUp(self):
get_setting_edit_handler.cache_clear()
def test_default_model_introspection(self):
handler = get_setting_edit_handler(TestGenericSetting)
self.assertIsInstance(handler, ObjectList)
self.assertEqual(len(handler.children), 2)
first = handler.children[0]
self.assertIsInstance(first, FieldPanel)
self.assertEqual(first.field_name, "title")
second = handler.children[1]
self.assertIsInstance(second, FieldPanel)
self.assertEqual(second.field_name, "email")
def test_with_custom_panels(self):
handler = get_setting_edit_handler(PanelGenericSettings)
self.assertIsInstance(handler, ObjectList)
self.assertEqual(len(handler.children), 1)
first = handler.children[0]
self.assertIsInstance(first, FieldPanel)
self.assertEqual(first.field_name, "title")
def test_with_custom_edit_handler(self):
handler = get_setting_edit_handler(TabbedGenericSettings)
self.assertIsInstance(handler, TabbedInterface)
self.assertEqual(len(handler.children), 2)

View File

@@ -0,0 +1,123 @@
from django.test import TestCase, override_settings
from wagtail.models import Site
from wagtail.test.testapp.models import ImportantPagesGenericSetting
from .base import GenericSettingsTestMixin
@override_settings(ALLOWED_HOSTS=["localhost", "other"])
class GenericSettingModelTestCase(GenericSettingsTestMixin, TestCase):
def _create_importantpagesgenericsetting_object(self):
return ImportantPagesGenericSetting.objects.create(
sign_up_page=self.default_site.root_page,
general_terms_page=self.default_site.root_page,
privacy_policy_page=self.other_site.root_page,
)
def test_request_or_site_with_site_returns_expected_settings(self):
for site, expected_settings in (
(self.default_site, self.default_settings),
(self.other_site, self.default_settings),
):
with self.subTest(site=site):
self.assertEqual(
self.default_settings.load(request_or_site=site),
expected_settings,
)
def test_request_or_site_with_request_returns_expected_settings(self):
default_site_request = self.get_request()
other_site_request = self.get_request(site=self.other_site)
for request, expected_settings in (
(default_site_request, self.default_settings),
(other_site_request, self.default_settings),
):
with self.subTest(request=request):
self.assertEqual(
self.default_settings.load(request_or_site=request),
expected_settings,
)
def test_request_or_site_with_request_result_caching(self):
# repeat test to show caching is unique per request instance,
# even when the requests are for the same site
for i, request in enumerate([self.get_request(), self.get_request()], 1):
with self.subTest(attempt=i):
# force site query beforehand
Site.find_for_request(request)
# only the first lookup should result in a query
with self.assertNumQueries(1):
for i in range(4):
self.default_settings.load(request_or_site=request)
def test_select_related(self, expected_queries=4):
"""The `select_related` attribute on setting models is `None` by default, so fetching foreign keys values requires additional queries"""
self._create_importantpagesgenericsetting_object()
# fetch settings and access foreign keys
with self.assertNumQueries(expected_queries):
settings = ImportantPagesGenericSetting.load()
settings.sign_up_page
settings.general_terms_page
settings.privacy_policy_page
def test_select_related_use_reduces_total_queries(self):
"""But, `select_related` can be used to reduce the number of queries needed to fetch foreign keys"""
try:
# set class attribute temporarily
ImportantPagesGenericSetting.select_related = [
"sign_up_page",
"general_terms_page",
"privacy_policy_page",
]
self.test_select_related(expected_queries=1)
finally:
# undo temporary change
ImportantPagesGenericSetting.select_related = None
def test_get_page_url_returns_page_urls(self):
self._create_importantpagesgenericsetting_object()
settings = ImportantPagesGenericSetting.load()
for page_fk_field, expected_result in (
("sign_up_page", "http://localhost/"),
("general_terms_page", "http://localhost/"),
("privacy_policy_page", "http://other/"),
):
with self.subTest(page_fk_field=page_fk_field):
self.assertEqual(settings.get_page_url(page_fk_field), expected_result)
self.assertEqual(
getattr(settings.page_url, page_fk_field), expected_result
)
def test_get_page_url_raises_attributeerror_if_attribute_name_invalid(self):
settings = self._create_importantpagesgenericsetting_object()
# when called directly
with self.assertRaises(AttributeError):
settings.get_page_url("not_an_attribute")
# when called indirectly via shortcut
with self.assertRaises(AttributeError):
settings.page_url.not_an_attribute
def test_get_page_url_returns_empty_string_if_attribute_value_not_a_page(self):
settings = self._create_importantpagesgenericsetting_object()
for value in (None, self.default_site):
with self.subTest(attribute_value=value):
settings.test_attribute = value
# when called directly
self.assertEqual(settings.get_page_url("test_attribute"), "")
# when called indirectly via shortcut
self.assertEqual(settings.page_url.test_attribute, "")
def test_display_as_string(self):
self._create_importantpagesgenericsetting_object()
self.assertEqual(
str(ImportantPagesGenericSetting.load()),
"Important pages settings",
)

View File

@@ -0,0 +1,24 @@
from django.test import TestCase
from django.urls import reverse
from wagtail.contrib.settings.registry import Registry
from wagtail.test.testapp.models import NotYetRegisteredGenericSetting
from wagtail.test.utils import WagtailTestUtils
class GenericSettingRegisterTestCase(WagtailTestUtils, TestCase):
def setUp(self):
self.registry = Registry()
self.login()
def test_register(self):
self.assertNotIn(NotYetRegisteredGenericSetting, self.registry)
NowRegisteredGenericSetting = self.registry.register_decorator(
NotYetRegisteredGenericSetting
)
self.assertIn(NotYetRegisteredGenericSetting, self.registry)
self.assertIs(NowRegisteredGenericSetting, NotYetRegisteredGenericSetting)
def test_icon(self):
admin = self.client.get(reverse("wagtailadmin_home"))
self.assertContains(admin, "icon-setting-tag")

View File

@@ -0,0 +1,197 @@
from django.template import Context, RequestContext, Template, engines
from django.test import TestCase
from django.test.utils import override_settings
from wagtail.coreutils import get_dummy_request
from wagtail.models import Site
from wagtail.test.utils import WagtailTestUtils
from .base import GenericSettingsTestMixin
@override_settings(ALLOWED_HOSTS=["testserver", "localhost", "other"])
class GenericSettingTemplateTestCase(
GenericSettingsTestMixin, WagtailTestUtils, TestCase
):
def render(self, request, string, context=None):
template = Template(string)
context = RequestContext(request, context)
return template.render(context)
class GenericSettingContextProcessorTestCase(GenericSettingTemplateTestCase):
def test_accessing_setting(self):
"""Check that the context processor works"""
request = self.get_request()
self.assertEqual(
self.render(request, "{{ settings.tests.TestGenericSetting.title }}"),
self.default_settings.title,
)
def test_model_case_insensitive(self):
"""Model names should be case insensitive"""
request = self.get_request()
self.assertEqual(
self.render(request, "{{ settings.tests.testgenericsetting.title }}"),
self.default_settings.title,
)
self.assertEqual(
self.render(request, "{{ settings.tests.TESTGENERICSETTING.title }}"),
self.default_settings.title,
)
self.assertEqual(
self.render(request, "{{ settings.tests.TestGenericSetting.title }}"),
self.default_settings.title,
)
self.assertEqual(
self.render(request, "{{ settings.tests.tEstgEnerICsEttIng.title }}"),
self.default_settings.title,
)
def test_models_cached(self):
"""Accessing a setting should only hit the DB once per request instance,
even if using that request to rendering multiple times"""
request = self.get_request()
get_title = "{{ settings.tests.testgenericsetting.title }}"
# force site query beforehand
Site.find_for_request(request)
with self.assertNumQueries(1):
for i in range(1, 4):
with self.subTest(attempt=i):
self.assertEqual(
self.render(request, get_title * i),
self.default_settings.title * i,
)
class GenericSettingTemplateTagTestCase(GenericSettingTemplateTestCase):
def test_no_context_processor(self):
"""
Assert that not running the context processor means settings are not in
the context, as expected.
"""
template = Template("{{ settings.tests.TestGenericSetting.title }}")
context = Context()
self.assertEqual(template.render(context), "")
def test_get_settings_request_context(self):
"""Check that the {% get_settings %} tag works"""
request = self.get_request()
context = Context({"request": request})
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings %}"
"{{ settings.tests.testgenericsetting.title }}"
)
self.assertEqual(template.render(context), self.default_settings.title)
def test_get_settings_no_request(self):
"""Check that the {% get_settings %} tag works with no request"""
context = Context()
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings %}"
"{{ settings.tests.testgenericsetting.title }}"
)
self.assertEqual(template.render(context), self.default_settings.title)
def test_get_settings_variable_assignment_request_context(self):
"""
Check that assigning the setting to a context variable with
{% get_settings as wagtail_settings %} works.
"""
request = self.get_request()
context = Context({"request": request})
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings as wagtail_settings %}"
"{{ wagtail_settings.tests.testgenericsetting.title }}"
)
self.assertEqual(template.render(context), self.default_settings.title)
# Also check that the default 'settings' variable hasn't been set
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings as wagtail_settings %}"
"{{ settings.tests.testgenericsetting.title }}"
)
self.assertEqual(template.render(context), "")
class GenericSettingJinjaContextProcessorTestCase(GenericSettingTemplateTestCase):
def setUp(self):
super().setUp()
self.engine = engines["jinja2"]
def render(self, string, context=None, request_context=True):
if context is None:
context = {}
# Add a request to the template, to simulate a RequestContext
if request_context:
site = Site.objects.get(is_default_site=True)
context["request"] = get_dummy_request(site=site)
template = self.engine.from_string(string)
return template.render(context)
def test_accessing_setting(self):
"""Check that the context processor works"""
self.assertEqual(
self.render('{{ settings("tests.TestGenericSetting").title }}'),
self.default_settings.title,
)
def test_model_case_insensitive(self):
"""Model names should be case insensitive"""
self.assertEqual(
self.render('{{ settings("tests.testgenericsetting").title }}'),
self.default_settings.title,
)
self.assertEqual(
self.render('{{ settings("tests.TESTGENERICSETTING").title }}'),
self.default_settings.title,
)
self.assertEqual(
self.render('{{ settings("tests.TestGenericSetting").title }}'),
self.default_settings.title,
)
self.assertEqual(
self.render('{{ settings("tests.tEstgEnerICsEttIng").title }}'),
self.default_settings.title,
)
def test_models_cached(self):
"""Accessing a setting should only hit the DB once per render"""
get_title = '{{ settings("tests.testgenericsetting").title }}'
request = self.get_request()
# run extra query before hand
Site.find_for_request(request)
for i in range(1, 4):
with self.assertNumQueries(1):
context = {"request": request}
template = self.engine.from_string(get_title * i)
self.assertEqual(
template.render(context), self.default_settings.title * i
)
def test_settings_no_request(self):
"""
Check that {{ settings }} does not throw an error if it can not find a
request to work with
"""
context = {}
template = '{{ settings("tests.testgenericsetting").title }}'
self.assertEqual(
self.render(template, context, request_context=False),
self.default_settings.title,
)

View File

@@ -0,0 +1,32 @@
from wagtail.coreutils import get_dummy_request
from wagtail.models import Page, Site
from wagtail.test.testapp.models import TestSiteSetting
class SiteSettingsTestMixin:
def setUp(self):
root = Page.objects.first()
other_home = Page(title="Other Root")
root.add_child(instance=other_home)
self.default_site = Site.objects.get(is_default_site=True)
self.default_settings = TestSiteSetting.objects.create(
title="Site title", email="initial@example.com", site=self.default_site
)
self.other_site = Site.objects.create(hostname="other", root_page=other_home)
self.other_settings = TestSiteSetting.objects.create(
title="Other title", email="other@other.com", site=self.other_site
)
def get_request(self, site=None):
if site is None:
site = self.default_site
request = get_dummy_request(site=site)
# Requests in general can't be pickled, but dummy requests can. Add an
# arbitrary lambda function to the request to make it fail loudly if
# someone tries to pickle it.
request._fn = lambda: None
return request

View File

@@ -0,0 +1,519 @@
from django.contrib.auth.models import Permission
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils.text import capfirst
from wagtail import hooks
from wagtail.admin.admin_url_finder import AdminURLFinder
from wagtail.admin.panels import FieldPanel, ObjectList, TabbedInterface
from wagtail.contrib.settings.registry import SettingMenuItem
from wagtail.contrib.settings.views import get_setting_edit_handler
from wagtail.models import Page, Site
from wagtail.test.testapp.models import (
FileSiteSetting,
IconSiteSetting,
PanelSiteSettings,
TabbedSiteSettings,
TestPermissionedSiteSetting,
TestSiteSetting,
)
from wagtail.test.utils import WagtailTestUtils
class TestSiteSettingMenu(WagtailTestUtils, TestCase):
def login_only_admin(self):
"""Log in with a user that only has permission to access the admin"""
user = self.create_user(username="test", password="password")
user.user_permissions.add(
Permission.objects.get_by_natural_key(
codename="access_admin", app_label="wagtailadmin", model="admin"
)
)
self.login(username="test", password="password")
return user
def test_menu_item_in_admin(self):
self.login()
response = self.client.get(reverse("wagtailadmin_home"))
self.assertContains(response, capfirst(TestSiteSetting._meta.verbose_name))
self.assertContains(
response, reverse("wagtailsettings:edit", args=("tests", "testsitesetting"))
)
def test_menu_item_no_permissions(self):
self.login_only_admin()
response = self.client.get(reverse("wagtailadmin_home"))
self.assertNotContains(response, TestSiteSetting._meta.verbose_name)
self.assertNotContains(
response, reverse("wagtailsettings:edit", args=("tests", "testsitesetting"))
)
def test_menu_item_icon(self):
menu_item = SettingMenuItem(IconSiteSetting, icon="tag", classname="test-class")
self.assertEqual(menu_item.icon_name, "tag")
self.assertEqual(menu_item.classname, "test-class")
class BaseTestSiteSettingView(WagtailTestUtils, TestCase):
def get(self, site_pk=1, params={}, setting=TestSiteSetting):
url = self.edit_url(setting=setting, site_pk=site_pk)
return self.client.get(url, params)
def post(self, site_pk=1, post_data={}, setting=TestSiteSetting):
url = self.edit_url(setting=setting, site_pk=site_pk)
return self.client.post(url, post_data)
def edit_url(self, setting, site_pk=1):
args = [setting._meta.app_label, setting._meta.model_name, site_pk]
return reverse("wagtailsettings:edit", args=args)
class TestSiteSettingCreateView(BaseTestSiteSettingView):
def setUp(self):
self.user = self.login()
def test_get_edit(self):
response = self.get()
self.assertEqual(response.status_code, 200)
def test_edit_invalid(self):
response = self.post(post_data={"foo": "bar"})
self.assertContains(response, "The setting could not be saved due to errors.")
self.assertContains(response, "error-message", count=2)
self.assertContains(response, "This field is required", count=2)
def test_edit(self):
response = self.post(
post_data={"title": "Edited site title", "email": "test@example.com"}
)
self.assertEqual(response.status_code, 302)
default_site = Site.objects.get(is_default_site=True)
setting = TestSiteSetting.objects.get(site=default_site)
self.assertEqual(setting.title, "Edited site title")
self.assertEqual(setting.email, "test@example.com")
url_finder = AdminURLFinder(self.user)
expected_url = "/admin/settings/tests/testsitesetting/%d/" % default_site.pk
self.assertEqual(url_finder.get_edit_url(setting), expected_url)
def test_file_upload_multipart(self):
response = self.get(setting=FileSiteSetting)
# Ensure the form supports file uploads
self.assertContains(response, 'enctype="multipart/form-data"')
def test_create_restricted_field_without_any_permission(self):
# User has no permissions over the setting model, only access to the admin
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
)
self.assertFalse(TestPermissionedSiteSetting.objects.exists())
# GET should redirect away with permission denied
response = self.get(setting=TestPermissionedSiteSetting)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# the GET might create a setting object, depending on when the permission check is done,
# so remove any created objects prior to testing the POST
# POST should redirect away with permission denied
response = self.post(
post_data={"sensitive_email": "test@example.com", "title": "test"},
setting=TestPermissionedSiteSetting,
)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# The retrieved setting should contain none of the submitted data
settings = TestPermissionedSiteSetting.for_site(Site.objects.get(pk=1))
self.assertEqual(settings.title, "")
self.assertEqual(settings.sensitive_email, "")
def test_create_restricted_field_without_field_permission(self):
# User has edit permission over the setting model, but not the sensitive_email field
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedsitesetting",
),
)
self.assertFalse(TestPermissionedSiteSetting.objects.exists())
# GET should provide a form with title but not sensitive_email
response = self.get(setting=TestPermissionedSiteSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertNotIn("sensitive_email", list(response.context["form"].fields))
# the GET creates a setting object, so remove any created objects prior to testing the POST
TestPermissionedSiteSetting.objects.all().delete()
# POST should allow the title to be set, but not the sensitive_email
response = self.post(
post_data={"sensitive_email": "test@example.com", "title": "test"},
setting=TestPermissionedSiteSetting,
)
self.assertEqual(response.status_code, 302)
settings = TestPermissionedSiteSetting.objects.get()
self.assertEqual(settings.title, "test")
self.assertEqual(settings.sensitive_email, "")
def test_create_restricted_field(self):
# User has edit permission over the setting model, including the sensitive_email field
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedsitesetting",
),
Permission.objects.get(codename="can_edit_sensitive_email_site_setting"),
)
self.assertFalse(TestPermissionedSiteSetting.objects.exists())
# GET should provide a form with title and sensitive_email
response = self.get(setting=TestPermissionedSiteSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertIn("sensitive_email", list(response.context["form"].fields))
# the GET creates a setting object, so remove any created objects prior to testing the POST
TestPermissionedSiteSetting.objects.all().delete()
# POST should allow both title and sensitive_email to be set
response = self.post(
post_data={"sensitive_email": "test@example.com", "title": "test"},
setting=TestPermissionedSiteSetting,
)
self.assertEqual(response.status_code, 302)
settings = TestPermissionedSiteSetting.objects.get()
self.assertEqual(settings.title, "test")
self.assertEqual(settings.sensitive_email, "test@example.com")
class TestSiteSettingEditView(BaseTestSiteSettingView):
def setUp(self):
self.default_site = Site.objects.get(is_default_site=True)
self.test_setting = TestSiteSetting()
self.test_setting.title = "Site title"
self.test_setting.email = "initial@example.com"
self.test_setting.site = self.default_site
self.test_setting.save()
self.user = self.login()
def test_get_edit(self):
response = self.get()
self.assertEqual(response.status_code, 200)
def test_non_existent_model(self):
response = self.client.get(
reverse("wagtailsettings:edit", args=["test", "foo", 1])
)
self.assertEqual(response.status_code, 404)
def test_register_with_icon(self):
edit_url = reverse("wagtailsettings:edit", args=("tests", "IconGenericSetting"))
edit_response = self.client.get(edit_url, follow=True)
self.assertContains(
edit_response,
"""<svg class="icon icon-icon-setting-tag w-header__glyph" aria-hidden="true"><use href="#icon-icon-setting-tag"></use></svg>""",
)
def test_edit_invalid(self):
response = self.post(post_data={"foo": "bar"})
self.assertContains(response, "The setting could not be saved due to errors.")
self.assertContains(response, "error-message", count=2)
self.assertContains(response, "This field is required", count=2)
def test_edit(self):
response = self.post(
post_data={"title": "Edited site title", "email": "test@example.com"}
)
self.assertEqual(response.status_code, 302)
default_site = Site.objects.get(is_default_site=True)
setting = TestSiteSetting.objects.get(site=default_site)
self.assertEqual(setting.title, "Edited site title")
self.assertEqual(setting.email, "test@example.com")
def test_get_redirect_to_relevant_instance(self):
url = reverse("wagtailsettings:edit", args=("tests", "testsitesetting"))
default_site = Site.objects.get(is_default_site=True)
response = self.client.get(url)
self.assertRedirects(
response, status_code=302, expected_url=f"{url}{default_site.pk}/"
)
def test_get_redirect_to_relevant_instance_invalid(self):
Site.objects.all().delete()
url = reverse("wagtailsettings:edit", args=("tests", "testsitesetting"))
response = self.client.get(url)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
def test_edit_restricted_field(self):
# User has edit permission over the setting model, including the sensitive_email field
test_setting = TestPermissionedSiteSetting()
test_setting.title = "Old title"
test_setting.sensitive_email = "test@example.com"
test_setting.site = self.default_site
test_setting.save()
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedsitesetting",
),
Permission.objects.get(codename="can_edit_sensitive_email_site_setting"),
)
# GET should provide a form with title and sensitive_email
response = self.get(setting=TestPermissionedSiteSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertIn("sensitive_email", list(response.context["form"].fields))
# POST should allow both title and sensitive_email to be set
response = self.post(
setting=TestPermissionedSiteSetting,
post_data={
"sensitive_email": "test-updated@example.com",
"title": "New title",
},
)
self.assertEqual(response.status_code, 302)
test_setting.refresh_from_db()
self.assertEqual(test_setting.sensitive_email, "test-updated@example.com")
self.assertEqual(test_setting.title, "New title")
def test_edit_restricted_field_without_field_permission(self):
# User has edit permission over the setting model, but not the sensitive_email field
test_setting = TestPermissionedSiteSetting()
test_setting.title = "Old title"
test_setting.sensitive_email = "test@example.com"
test_setting.site = self.default_site
test_setting.save()
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
Permission.objects.get(
content_type__app_label="tests",
codename="change_testpermissionedsitesetting",
),
)
# GET should provide a form with title but not sensitive_email
response = self.get(setting=TestPermissionedSiteSetting)
self.assertEqual(response.status_code, 200)
self.assertIn("title", list(response.context["form"].fields))
self.assertNotIn("sensitive_email", list(response.context["form"].fields))
# POST should allow the title to be set, but not the sensitive_email
response = self.post(
setting=TestPermissionedSiteSetting,
post_data={
"sensitive_email": "test-updated@example.com",
"title": "New title",
},
)
self.assertEqual(response.status_code, 302)
test_setting.refresh_from_db()
self.assertEqual(test_setting.sensitive_email, "test@example.com")
self.assertEqual(test_setting.title, "New title")
def test_edit_restricted_field_without_any_permission(self):
# User has no permissions over the setting model, only access to the admin
test_setting = TestPermissionedSiteSetting()
test_setting.title = "Old title"
test_setting.sensitive_email = "test@example.com"
test_setting.site = self.default_site
test_setting.save()
self.user.is_superuser = False
self.user.save()
self.user.user_permissions.add(
Permission.objects.get(
content_type__app_label="wagtailadmin", codename="access_admin"
),
)
# GET should redirect away with permission denied
response = self.get(setting=TestPermissionedSiteSetting)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# POST should redirect away with permission denied
response = self.post(
setting=TestPermissionedSiteSetting,
post_data={
"sensitive_email": "test-updated@example.com",
"title": "New title",
},
)
self.assertRedirects(response, status_code=302, expected_url="/admin/")
# The retrieved setting should be unchanged
test_setting.refresh_from_db()
self.assertEqual(test_setting.sensitive_email, "test@example.com")
self.assertEqual(test_setting.title, "Old title")
@override_settings(
ALLOWED_HOSTS=["testserver", "example.com", "noneoftheabove.example.com"]
)
class TestMultiSite(BaseTestSiteSettingView):
def setUp(self):
self.default_site = Site.objects.get(is_default_site=True)
self.other_site = Site.objects.create(
hostname="example.com", root_page=Page.objects.get(pk=2)
)
self.login()
def test_redirect_to_default(self):
"""
Should redirect to the setting for the default site.
"""
start_url = reverse("wagtailsettings:edit", args=["tests", "testsitesetting"])
dest_url = reverse(
"wagtailsettings:edit",
args=["tests", "testsitesetting", self.default_site.pk],
)
response = self.client.get(start_url, follow=True)
self.assertRedirects(
response, dest_url, status_code=302, fetch_redirect_response=False
)
def test_redirect_to_current(self):
"""
Should redirect to the setting for the current site taken from the URL,
by default
"""
start_url = reverse("wagtailsettings:edit", args=["tests", "testsitesetting"])
dest_url = reverse(
"wagtailsettings:edit",
args=["tests", "testsitesetting", self.other_site.pk],
)
response = self.client.get(
start_url, follow=True, HTTP_HOST=self.other_site.hostname
)
self.assertRedirects(
response, dest_url, status_code=302, fetch_redirect_response=False
)
def test_with_no_current_site(self):
"""
Redirection should not break if the current request does not correspond to a site
"""
self.default_site.is_default_site = False
self.default_site.save()
start_url = reverse("wagtailsettings:edit", args=["tests", "testsitesetting"])
response = self.client.get(
start_url, follow=True, HTTP_HOST="noneoftheabove.example.com"
)
self.assertEqual(302, response.redirect_chain[0][1])
def test_switcher(self):
"""Check that the switcher form exists in the page"""
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'id="settings-site-switch"')
def test_unknown_site(self):
"""Check that unknown sites throw a 404"""
response = self.get(site_pk=3)
self.assertEqual(response.status_code, 404)
def test_edit(self):
"""
Check that editing settings in multi-site mode edits the correct
setting, and leaves the other ones alone
"""
TestSiteSetting.objects.create(
title="default", email="default@example.com", site=self.default_site
)
TestSiteSetting.objects.create(
title="other", email="other@example.com", site=self.other_site
)
response = self.post(
site_pk=self.other_site.pk,
post_data={"title": "other-new", "email": "other-other@example.com"},
)
self.assertEqual(response.status_code, 302)
# Check that the correct setting was updated
other_setting = TestSiteSetting.for_site(self.other_site)
self.assertEqual(other_setting.title, "other-new")
self.assertEqual(other_setting.email, "other-other@example.com")
# Check that the other setting was not updated
default_setting = TestSiteSetting.for_site(self.default_site)
self.assertEqual(default_setting.title, "default")
self.assertEqual(default_setting.email, "default@example.com")
class TestAdminPermission(WagtailTestUtils, TestCase):
def test_registered_permission(self):
permission = Permission.objects.get_by_natural_key(
app_label="tests",
model="testsitesetting",
codename="change_testsitesetting",
)
for fn in hooks.get_hooks("register_permissions"):
if permission in fn():
break
else:
self.fail("Change permission for tests.TestSiteSetting not registered")
class TestEditHandlers(TestCase):
def setUp(self):
get_setting_edit_handler.cache_clear()
def test_default_model_introspection(self):
handler = get_setting_edit_handler(TestSiteSetting)
self.assertIsInstance(handler, ObjectList)
self.assertEqual(len(handler.children), 2)
first = handler.children[0]
self.assertIsInstance(first, FieldPanel)
self.assertEqual(first.field_name, "title")
second = handler.children[1]
self.assertIsInstance(second, FieldPanel)
self.assertEqual(second.field_name, "email")
def test_with_custom_panels(self):
handler = get_setting_edit_handler(PanelSiteSettings)
self.assertIsInstance(handler, ObjectList)
self.assertEqual(len(handler.children), 1)
first = handler.children[0]
self.assertIsInstance(first, FieldPanel)
self.assertEqual(first.field_name, "title")
def test_with_custom_edit_handler(self):
handler = get_setting_edit_handler(TabbedSiteSettings)
self.assertIsInstance(handler, TabbedInterface)
self.assertEqual(len(handler.children), 2)

View File

@@ -0,0 +1,31 @@
from django.test import TestCase
from wagtail.contrib.settings.forms import SiteSwitchForm
from wagtail.models import Page, Site
from wagtail.test.testapp.models import TestSiteSetting
class TestSiteSwitchFromSiteOrdering(TestCase):
def setUp(self):
self.root_page = Page.objects.get(pk=2)
Site.objects.all().delete() # Drop the initial site.
def test_site_order_by_hostname(self):
site_1 = Site.objects.create(hostname="charly.com", root_page=self.root_page)
site_2 = Site.objects.create(
hostname="bravo.com", root_page=self.root_page, is_default_site=True
)
site_3 = Site.objects.create(hostname="alfa.com", root_page=self.root_page)
form = SiteSwitchForm(site_1, TestSiteSetting)
expected_choices = [
(f"/admin/settings/tests/testsitesetting/{site_3.id}/", "alfa.com"),
(
f"/admin/settings/tests/testsitesetting/{site_2.id}/",
"bravo.com [default]",
),
(
f"/admin/settings/tests/testsitesetting/{site_1.id}/",
"charly.com",
),
]
self.assertEqual(form.fields["site"].choices, expected_choices)

View File

@@ -0,0 +1,225 @@
import pickle
from django.test import RequestFactory, TestCase, override_settings
from wagtail.models import Site
from wagtail.test.testapp.models import ImportantPagesSiteSetting, TestSiteSetting
from .base import SiteSettingsTestMixin
@override_settings(ALLOWED_HOSTS=["localhost", "other"])
class SettingModelTestCase(SiteSettingsTestMixin, TestCase):
def test_for_site_returns_expected_settings(self):
for site, expected_settings in (
(self.default_site, self.default_settings),
(self.other_site, self.other_settings),
):
with self.subTest(site=site):
self.assertEqual(TestSiteSetting.for_site(site), expected_settings)
@override_settings(ALLOWED_HOSTS=["no-site-match.example"])
def test_for_request_raises_does_not_exist_when_no_site_match(self):
Site.objects.update(is_default_site=False)
# Use RequestFactory directly, as self.get_request sets SERVER_NAME and site.
request = RequestFactory(SERVER_NAME="no-site-match.example").get("/")
with self.assertRaises(TestSiteSetting.DoesNotExist):
TestSiteSetting.for_request(request)
def test_for_site_raises_does_not_exist_when_site_is_none(self):
with self.assertRaises(TestSiteSetting.DoesNotExist):
TestSiteSetting.for_site(None)
def test_for_request_returns_expected_settings(self):
default_site_request = self.get_request()
other_site_request = self.get_request(site=self.other_site)
for request, expected_settings in (
(default_site_request, self.default_settings),
(other_site_request, self.other_settings),
):
with self.subTest(request=request):
self.assertEqual(
TestSiteSetting.for_request(request), expected_settings
)
def test_for_request_result_caching(self):
# repeat test to show caching is unique per request instance,
# even when the requests are for the same site
for i, request in enumerate([self.get_request(), self.get_request()], 1):
with self.subTest(attempt=i):
# force site query beforehand
Site.find_for_request(request)
# only the first lookup should result in a query
with self.assertNumQueries(1):
for i in range(4):
TestSiteSetting.for_request(request)
def test_pickle_after_lookup_via_for_request(self):
request = self.get_request()
settings = TestSiteSetting.for_request(request)
pickled = pickle.dumps(settings)
unpickled = pickle.loads(pickled)
self.assertEqual(unpickled.title, "Site title")
def _create_importantpagessitesetting_object(self):
site = self.default_site
return ImportantPagesSiteSetting.objects.create(
site=site,
sign_up_page=site.root_page,
general_terms_page=site.root_page,
privacy_policy_page=self.other_site.root_page,
)
def test_importantpages_object_is_pickleable(self):
obj = self._create_importantpagessitesetting_object()
# Triggers creation of the InvokeViaAttributeShortcut instance,
# and also gives us a value we can use for comparison
signup_page_url = obj.page_url.sign_up_page
# Attempt to pickle ImportantPages instance
try:
pickled = pickle.dumps(obj, -1)
except Exception as e: # noqa: BLE001
raise AssertionError(
f"An error occurred when attempting to pickle {obj!r}: {e}"
)
# Now unpickle the pickled ImportantPages
try:
unpickled = pickle.loads(pickled)
except Exception as e: # noqa: BLE001
raise AssertionError(
f"An error occurred when attempting to unpickle {obj!r}: {e}"
)
# Using 'page_url' should create a new InvokeViaAttributeShortcut
# instance, which should give the same result as the original
self.assertEqual(
unpickled.page_url.sign_up_page,
signup_page_url,
)
def test_select_related(self, expected_queries=4):
"""The `select_related` attribute on setting models is `None` by default, so fetching foreign keys values requires additional queries"""
request = self.get_request()
self._create_importantpagessitesetting_object()
# force site query beforehand
Site.find_for_request(request)
# fetch settings and access foreiegn keys
with self.assertNumQueries(expected_queries):
settings = ImportantPagesSiteSetting.for_request(request)
settings.sign_up_page
settings.general_terms_page
settings.privacy_policy_page
def test_select_related_use_reduces_total_queries(self):
"""But, `select_related` can be used to reduce the number of queries needed to fetch foreign keys"""
try:
# set class attribute temporarily
ImportantPagesSiteSetting.select_related = [
"sign_up_page",
"general_terms_page",
"privacy_policy_page",
]
self.test_select_related(expected_queries=1)
finally:
# undo temporary change
ImportantPagesSiteSetting.select_related = None
def test_get_page_url_when_settings_fetched_via_for_request(self):
"""Using ImportantPagesSiteSetting.for_request() makes the setting
object request-aware, improving efficiency and allowing
site-relative URLs to be returned"""
self._create_importantpagessitesetting_object()
request = self.get_request()
settings = ImportantPagesSiteSetting.for_request(request)
# Force site root paths query beforehand
self.default_site.root_page._get_site_root_paths(request)
for page_fk_field, expected_result in (
("sign_up_page", "/"),
("general_terms_page", "/"),
("privacy_policy_page", "http://other/"),
):
with self.subTest(page_fk_field=page_fk_field):
with self.assertNumQueries(1):
# because results are cached, only the first
# request for a URL will trigger a query to
# fetch the page
self.assertEqual(
settings.get_page_url(page_fk_field), expected_result
)
# when called directly
self.assertEqual(
settings.get_page_url(page_fk_field), expected_result
)
# when called indirectly via shortcut
self.assertEqual(
getattr(settings.page_url, page_fk_field), expected_result
)
def test_get_page_url_when_for_settings_fetched_via_for_site(self):
"""ImportantPagesSiteSetting.for_site() cannot make the settings object
request-aware, so things are a little less efficient, and the
URLs returned will not be site-relative"""
self._create_importantpagessitesetting_object()
settings = ImportantPagesSiteSetting.for_site(self.default_site)
# Force site root paths query beforehand
self.default_site.root_page._get_site_root_paths()
for page_fk_field, expected_result in (
("sign_up_page", "http://localhost/"),
("general_terms_page", "http://localhost/"),
("privacy_policy_page", "http://other/"),
):
with self.subTest(page_fk_field=page_fk_field):
# only the first request for each URL will trigger queries.
# 2 are triggered instead of 1 here, because tests use the
# database cache backed, and the cache is queried each time
# to fetch site root paths (because there's no 'request' to
# store them on)
with self.assertNumQueries(2):
self.assertEqual(
settings.get_page_url(page_fk_field), expected_result
)
# when called directly
self.assertEqual(
settings.get_page_url(page_fk_field), expected_result
)
# when called indirectly via shortcut
self.assertEqual(
getattr(settings.page_url, page_fk_field), expected_result
)
def test_get_page_url_raises_attributeerror_if_attribute_name_invalid(self):
settings = self._create_importantpagessitesetting_object()
# when called directly
with self.assertRaises(AttributeError):
settings.get_page_url("not_an_attribute")
# when called indirectly via shortcut
with self.assertRaises(AttributeError):
settings.page_url.not_an_attribute
def test_get_page_url_returns_empty_string_if_attribute_value_not_a_page(self):
settings = self._create_importantpagessitesetting_object()
for value in (None, self.default_site):
with self.subTest(attribute_value=value):
settings.test_attribute = value
# when called directly
self.assertEqual(settings.get_page_url("test_attribute"), "")
# when called indirectly via shortcut
self.assertEqual(settings.page_url.test_attribute, "")

View File

@@ -0,0 +1,24 @@
from django.test import TestCase
from django.urls import reverse
from wagtail.contrib.settings.registry import Registry
from wagtail.test.testapp.models import NotYetRegisteredSiteSetting
from wagtail.test.utils import WagtailTestUtils
class TestRegister(WagtailTestUtils, TestCase):
def setUp(self):
self.registry = Registry()
self.login()
def test_register(self):
self.assertNotIn(NotYetRegisteredSiteSetting, self.registry)
NowRegisteredSetting = self.registry.register_decorator(
NotYetRegisteredSiteSetting
)
self.assertIn(NotYetRegisteredSiteSetting, self.registry)
self.assertIs(NowRegisteredSetting, NotYetRegisteredSiteSetting)
def test_icon(self):
admin = self.client.get(reverse("wagtailadmin_home"))
self.assertContains(admin, "icon-setting-tag")

View File

@@ -0,0 +1,317 @@
from django.template import Context, RequestContext, Template, engines
from django.test import TestCase
from django.test.utils import override_settings
from wagtail.coreutils import get_dummy_request
from wagtail.models import Site
from wagtail.test.utils import WagtailTestUtils
from .base import SiteSettingsTestMixin
@override_settings(ALLOWED_HOSTS=["testserver", "localhost", "other"])
class TemplateTestCase(SiteSettingsTestMixin, WagtailTestUtils, TestCase):
def render(self, request, string, context=None, site=None):
template = Template(string)
context = RequestContext(request, context)
return template.render(context)
class TestContextProcessor(TemplateTestCase):
def test_accessing_setting(self):
"""Check that the context processor works"""
request = self.get_request()
self.assertEqual(
self.render(request, "{{ settings.tests.TestSiteSetting.title }}"),
self.default_settings.title,
)
def test_multi_site(self):
"""Check that the correct setting for the current site is returned"""
request = self.get_request(site=self.default_site)
self.assertEqual(
self.render(request, "{{ settings.tests.TestSiteSetting.title }}"),
self.default_settings.title,
)
request = self.get_request(site=self.other_site)
self.assertEqual(
self.render(request, "{{ settings.tests.TestSiteSetting.title }}"),
self.other_settings.title,
)
def test_model_case_insensitive(self):
"""Model names should be case insensitive"""
request = self.get_request()
self.assertEqual(
self.render(request, "{{ settings.tests.testsitesetting.title }}"),
self.default_settings.title,
)
self.assertEqual(
self.render(request, "{{ settings.tests.TESTSITESETTING.title }}"),
self.default_settings.title,
)
self.assertEqual(
self.render(request, "{{ settings.tests.TestSiteSetting.title }}"),
self.default_settings.title,
)
self.assertEqual(
self.render(request, "{{ settings.tests.tEstSiTesEttIng.title }}"),
self.default_settings.title,
)
def test_models_cached(self):
"""Accessing a setting should only hit the DB once per request instance,
even if using that request to rendering multiple times"""
request = self.get_request()
get_title = "{{ settings.tests.testsitesetting.title }}"
# force site query beforehand
Site.find_for_request(request)
with self.assertNumQueries(1):
for i in range(1, 4):
with self.subTest(attempt=i):
self.assertEqual(
self.render(request, get_title * i),
self.default_settings.title * i,
)
class TestTemplateTag(TemplateTestCase):
def test_no_context_processor(self):
"""
Assert that not running the context processor means settings are not in
the context, as expected.
"""
template = Template("{{ settings.tests.TestSiteSetting.title }}")
context = Context()
self.assertEqual(template.render(context), "")
def test_get_settings_request_context(self):
"""Check that the {% get_settings %} tag works"""
request = self.get_request(site=self.other_site)
context = Context({"request": request})
# This should use the site in the request
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings %}"
"{{ settings.tests.testsitesetting.title }}"
)
self.assertEqual(template.render(context), self.other_settings.title)
def test_get_settings_request_context_use_default(self):
"""
Check that the {% get_settings use_default_site=True %} option
overrides a request in the context.
"""
request = self.get_request(site=self.other_site)
context = Context({"request": request})
# This should use the default site, ignoring the site in the request
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings use_default_site=True %}"
"{{ settings.tests.testsitesetting.title }}"
)
self.assertEqual(template.render(context), self.default_settings.title)
def test_get_settings_use_default(self):
"""
Check that the {% get_settings use_default_site=True %} option works
"""
context = Context()
# This should use the default site
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings use_default_site=True %}"
"{{ settings.tests.testsitesetting.title }}"
)
self.assertEqual(template.render(context), self.default_settings.title)
def test_get_settings_no_request_no_default(self):
"""
Check that the {% get_settings %} throws an error if it can not find a
site to work with
"""
context = Context()
# Without a request in the context, and without use_default_site, this
# should bail with an error
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings %}"
"{{ settings.tests.testsitesetting.title }}"
)
with self.assertRaises(RuntimeError):
template.render(context)
def test_get_settings_variable_assignment_request_context(self):
"""
Check that assigning the setting to a context variable with
{% get_settings as wagtail_settings %} works.
"""
request = self.get_request(site=self.other_site)
context = Context({"request": request})
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings as wagtail_settings %}"
"{{ wagtail_settings.tests.testsitesetting.title }}"
)
self.assertEqual(template.render(context), self.other_settings.title)
# Also check that the default 'settings' variable hasn't been set
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings as wagtail_settings %}"
"{{ settings.tests.testsitesetting.title }}"
)
self.assertEqual(template.render(context), "")
def test_get_settings_variable_assignment_use_default(self):
"""
Check that assigning the setting to a context variable with
{% get_settings use_default_site=True as wagtail_settings %} works.
"""
context = Context()
template = Template(
"{% load wagtailsettings_tags %}"
"{% get_settings use_default_site=True as wagtail_settings %}"
"{{ wagtail_settings.tests.testsitesetting.title }}"
)
self.assertEqual(template.render(context), self.default_settings.title)
class TestSiteSettingsJinja(TemplateTestCase):
def setUp(self):
super().setUp()
self.engine = engines["jinja2"]
def render(self, string, context=None, request_context=True):
if context is None:
context = {}
# Add a request to the template, to simulate a RequestContext
if request_context:
if "site" in context:
site = context["site"]
else:
site = Site.objects.get(is_default_site=True)
context["request"] = get_dummy_request(site=site)
template = self.engine.from_string(string)
return template.render(context)
def test_accessing_setting(self):
"""Check that the context processor works"""
self.assertEqual(
self.render('{{ settings("tests.TestSiteSetting").title }}'),
self.default_settings.title,
)
def test_multi_site(self):
"""Check that the correct setting for the current site is returned"""
context = {"site": self.default_site}
self.assertEqual(
self.render(
'{{ settings("tests.TestSiteSetting").title }}',
context,
),
self.default_settings.title,
)
context = {"site": self.other_site}
self.assertEqual(
self.render(
'{{ settings("tests.TestSiteSetting").title }}',
context,
),
self.other_settings.title,
)
def test_model_case_insensitive(self):
"""Model names should be case insensitive"""
self.assertEqual(
self.render('{{ settings("tests.testsitesetting").title }}'),
self.default_settings.title,
)
self.assertEqual(
self.render('{{ settings("tests.TESTSITESETTING").title }}'),
self.default_settings.title,
)
self.assertEqual(
self.render('{{ settings("tests.TestSiteSetting").title }}'),
self.default_settings.title,
)
self.assertEqual(
self.render('{{ settings("tests.tEstSiTesEttIng").title }}'),
self.default_settings.title,
)
def test_models_cached(self):
"""Accessing a setting should only hit the DB once per render"""
get_title = '{{ settings("tests.testsitesetting").title }}'
request = self.get_request()
# run extra query before hand
Site.find_for_request(request)
for i in range(1, 4):
with self.assertNumQueries(1):
context = {"request": request}
template = self.engine.from_string(get_title * i)
self.assertEqual(
template.render(context), self.default_settings.title * i
)
def test_settings_use_default_site_override(self):
"""
Check that {{ settings(use_default_site=True) }} overrides a site in
the context.
"""
request = self.get_request(site=self.other_site)
context = {"request": request}
# This should use the default site, ignoring the site in the request
template = (
'{{ settings("tests.testsitesetting", use_default_site=True).title }}'
)
self.assertEqual(self.render(template, context), self.default_settings.title)
def test_settings_use_default_site(self):
"""
Check that the {{ settings(use_default_site=True) }} option works with
no site in the context
"""
context = {}
# This should use the default site
template = (
'{{ settings("tests.testsitesetting", use_default_site=True).title }}'
)
self.assertEqual(
self.render(template, context, request_context=False),
self.default_settings.title,
)
def test_settings_no_request_no_use_default(self):
"""
Check that {{ settings }} throws an error if it can not find a
site to work with
"""
context = {}
# Without a request in the context, and without use_default_site, this
# should bail with an error
template = '{{ settings("tests.testsitesetting").title }}'
with self.assertRaises(RuntimeError):
self.render(template, context, request_context=False)