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, '