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.generic.preview import PreviewOnEdit
from wagtail.test.testapp.models import (
EventCategory,
MultiPreviewModesModel,
NonPreviewableModel,
PreviewableModel,
RevisableModel,
)
from wagtail.test.utils import WagtailTestUtils
class TestPreview(WagtailTestUtils, TestCase):
def setUp(self):
self.user = self.login()
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.snippet = PreviewableModel.objects.create(text="A previewable snippet")
self.preview_on_add_url = reverse(
"wagtailsnippets_tests_previewablemodel:preview_on_add"
)
self.preview_on_edit_url = reverse(
"wagtailsnippets_tests_previewablemodel:preview_on_edit",
args=(self.snippet.pk,),
)
self.session_key_prefix = "wagtail-preview-tests-previewablemodel"
self.edit_session_key = f"{self.session_key_prefix}-{self.snippet.pk}"
self.post_data = {
"text": "An edited previewable snippet",
"categories": [self.parties_category.id, self.holidays_category.id],
}
def test_preview_on_create_with_no_session_data(self):
self.assertNotIn(self.session_key_prefix, self.client.session)
response = self.client.get(self.preview_on_add_url)
# The preview should be unavailable
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
self.assertContains(
response,
"
Preview not available - Wagtail",
html=True,
)
self.assertContains(
response,
'Preview not available
',
html=True,
)
def test_preview_on_create_with_invalid_data(self):
self.assertNotIn(self.session_key_prefix, self.client.session)
response = self.client.post(self.preview_on_add_url, {"text": ""})
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content.decode(),
{"is_valid": False, "is_available": False},
)
# The invalid data should not be saved in the session
self.assertNotIn(self.session_key_prefix, self.client.session)
response = self.client.get(self.preview_on_add_url)
# The preview should still be unavailable
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
self.assertContains(
response,
"Preview not available - Wagtail",
html=True,
)
self.assertContains(
response,
'Preview not available
',
html=True,
)
def test_preview_on_create_with_m2m_field(self):
response = self.client.post(self.preview_on_add_url, self.post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content.decode(),
{"is_valid": True, "is_available": True},
)
# Check the user can refresh the preview
self.assertIn(self.session_key_prefix, self.client.session)
response = self.client.get(self.preview_on_add_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "tests/previewable_model.html")
self.assertContains(response, "An edited previewable snippet")
self.assertContains(response, "Parties")
self.assertContains(response, "Holidays")
def test_preview_on_edit_with_m2m_field(self):
response = self.client.post(self.preview_on_edit_url, self.post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content.decode(),
{"is_valid": True, "is_available": True},
)
# Check the user can refresh the preview
self.assertIn(self.edit_session_key, self.client.session)
response = self.client.get(self.preview_on_edit_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "tests/previewable_model.html")
self.assertContains(response, "An edited previewable snippet")
self.assertContains(response, "Parties")
self.assertContains(response, "Holidays")
def test_preview_on_edit_with_valid_then_invalid_data(self):
response = self.client.post(self.preview_on_edit_url, self.post_data)
# Check the JSON response
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content.decode(),
{"is_valid": True, "is_available": True},
)
# Send an invalid update request
response = self.client.post(
self.preview_on_edit_url, {**self.post_data, "text": ""}
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content.decode(),
{"is_valid": False, "is_available": True},
)
# Check the user can still see the preview with the last valid data
self.assertIn(self.edit_session_key, self.client.session)
response = self.client.get(self.preview_on_edit_url)
# Check the HTML response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "tests/previewable_model.html")
self.assertContains(response, "An edited previewable snippet")
self.assertContains(response, "Parties")
self.assertContains(response, "Holidays")
def test_preview_on_edit_expiry(self):
initial_datetime = timezone.now()
expiry_datetime = initial_datetime + datetime.timedelta(
seconds=PreviewOnEdit.preview_expiration_timeout + 1
)
new_snippet = PreviewableModel.objects.create(text="A new previewable snippet")
with freeze_time(initial_datetime) as frozen_datetime:
response = self.client.post(self.preview_on_edit_url, self.post_data)
self.assertEqual(response.status_code, 200)
response = self.client.get(self.preview_on_edit_url)
self.assertEqual(response.status_code, 200)
frozen_datetime.move_to(expiry_datetime)
preview_url = reverse(
"wagtailsnippets_tests_previewablemodel:preview_on_edit",
args=(new_snippet.pk,),
)
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)
# Stale preview data should be removed from the session
self.assertNotIn(self.edit_session_key, self.client.session)
self.assertIn(
f"{self.session_key_prefix}-{new_snippet.pk}",
self.client.session,
)
def test_preview_on_create_clear_preview_data(self):
# Set a fake preview session data for the page
self.client.session[self.session_key_prefix] = "test data"
response = self.client.delete(self.preview_on_add_url)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content.decode(),
{"success": True},
)
# The data should no longer exist in the session
self.assertNotIn(self.session_key_prefix, self.client.session)
response = self.client.get(self.preview_on_add_url)
# The preview should be unavailable
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
self.assertContains(
response,
"Preview not available - Wagtail",
html=True,
)
self.assertContains(
response,
'Preview not available
',
html=True,
)
def test_preview_on_edit_clear_preview_data(self):
# Set a fake preview session data for the page
self.client.session[self.edit_session_key] = "test data"
response = self.client.delete(self.preview_on_edit_url)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content.decode(),
{"success": True},
)
# The data should no longer exist in the session
self.assertNotIn(self.edit_session_key, self.client.session)
response = self.client.get(self.preview_on_edit_url)
# The preview should be unavailable
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
self.assertContains(
response,
"Preview not available - Wagtail",
html=True,
)
self.assertContains(
response,
'Preview not available
',
html=True,
)
def test_preview_revision(self):
snippet = MultiPreviewModesModel.objects.create(text="Multiple modes")
revision = snippet.save_revision(log_action=True)
response = self.client.get(
reverse(
"wagtailsnippets_tests_multipreviewmodesmodel:revisions_view",
args=(snippet.pk, revision.id),
)
)
self.assertEqual(response.status_code, 200)
# Should respect the default_preview_mode
self.assertTemplateUsed(response, "tests/previewable_model_alt.html")
self.assertContains(response, "Multiple modes (Alternate Preview)")
class TestEnablePreview(WagtailTestUtils, TestCase):
def setUp(self):
self.user = self.login()
self.single = PreviewableModel.objects.create(text="Single preview mode")
self.multiple = MultiPreviewModesModel.objects.create(
text="Multiple preview modes"
)
def get_url(self, snippet, name, args=None):
model_name = type(snippet)._meta.model_name
return reverse(f"wagtailsnippets_tests_{model_name}:{name}", args=args)
def test_show_preview_panel_on_create_with_single_mode(self):
create_url = self.get_url(self.single, "add")
preview_url = self.get_url(self.single, "preview_on_add")
new_tab_url = preview_url + "?mode="
response = self.client.get(create_url)
self.assertEqual(response.status_code, 200)
# Should show the preview panel
self.assertContains(response, 'data-side-panel="preview"')
self.assertContains(response, 'data-action="%s"' % preview_url)
# Should have the preview side panel toggle button
soup = self.get_soup(response.content)
toggle_button = soup.find("button", {"data-side-panel-toggle": "preview"})
self.assertIsNotNone(toggle_button)
self.assertEqual("w-tooltip w-kbd", toggle_button["data-controller"])
self.assertEqual("mod+p", toggle_button["data-w-kbd-key-value"])
# Should show the iframe
self.assertContains(
response,
'