1import datetime
2
3from unittest import mock
4
5from django.contrib.auth.models import Group, Permission
6from django.http import HttpRequest, HttpResponse
7from django.test import TestCase
8from django.test.utils import override_settings
9from django.urls import reverse
10from django.utils import timezone
11from django.utils.translation import gettext_lazy as _
12
13from wagtail.admin.tests.pages.timestamps import submittable_timestamp
14from wagtail.core.models import GroupPagePermission, Locale, Page, PageRevision
15from wagtail.core.signals import page_published
16from wagtail.tests.testapp.models import (
17    BusinessChild, BusinessIndex, BusinessSubIndex, DefaultStreamPage, PersonPage, SimplePage,
18    SingletonPage, SingletonPageViaMaxCount, StandardChild, StandardIndex)
19from wagtail.tests.utils import WagtailTestUtils
20
21
22class TestPageCreation(TestCase, WagtailTestUtils):
23    def setUp(self):
24        # Find root page
25        self.root_page = Page.objects.get(id=2)
26
27        # Login
28        self.user = self.login()
29
30    def test_add_subpage(self):
31        response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.root_page.id, )))
32        self.assertEqual(response.status_code, 200)
33
34        self.assertContains(response, "Simple page")
35        target_url = reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id))
36        self.assertContains(response, 'href="%s"' % target_url)
37        # List of available page types should not contain pages with is_creatable = False
38        self.assertNotContains(response, "MTI base page")
39        # List of available page types should not contain abstract pages
40        self.assertNotContains(response, "Abstract page")
41        # List of available page types should not contain pages whose parent_page_types forbid it
42        self.assertNotContains(response, "Business child")
43
44    def test_add_subpage_with_subpage_types(self):
45        # Add a BusinessIndex to test business rules in
46        business_index = BusinessIndex(
47            title="Hello world!",
48            slug="hello-world",
49        )
50        self.root_page.add_child(instance=business_index)
51
52        response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(business_index.id, )))
53        self.assertEqual(response.status_code, 200)
54
55        self.assertContains(response, "Business child")
56        # List should not contain page types not in the subpage_types list
57        self.assertNotContains(response, "Simple page")
58
59    def test_add_subpage_with_one_valid_subpage_type(self):
60        # Add a BusinessSubIndex to test business rules in
61        business_index = BusinessIndex(
62            title="Hello world!",
63            slug="hello-world",
64        )
65        self.root_page.add_child(instance=business_index)
66        business_subindex = BusinessSubIndex(
67            title="Hello world!",
68            slug="hello-world",
69        )
70        business_index.add_child(instance=business_subindex)
71
72        response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(business_subindex.id, )))
73        # Should be redirected to the 'add' page for BusinessChild, the only valid subpage type
74        self.assertRedirects(
75            response,
76            reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', business_subindex.id))
77        )
78
79    def test_add_subpage_bad_permissions(self):
80        # Remove privileges from user
81        self.user.is_superuser = False
82        self.user.user_permissions.add(
83            Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
84        )
85        self.user.save()
86
87        # Get add subpage page
88        response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.root_page.id, )))
89
90        # Check that the user received a 403 response
91        self.assertEqual(response.status_code, 302)
92
93    def test_add_subpage_nonexistantparent(self):
94        response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(100000, )))
95        self.assertEqual(response.status_code, 404)
96
97    def test_add_subpage_with_next_param(self):
98        response = self.client.get(
99            reverse('wagtailadmin_pages:add_subpage', args=(self.root_page.id, )),
100            {'next': '/admin/users/'}
101        )
102        self.assertEqual(response.status_code, 200)
103
104        self.assertContains(response, "Simple page")
105        target_url = reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id))
106        self.assertContains(response, 'href="%s?next=/admin/users/"' % target_url)
107
108    def test_create_simplepage(self):
109        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
110        self.assertEqual(response.status_code, 200)
111        self.assertEqual(response['Content-Type'], "text/html; charset=utf-8")
112        self.assertContains(response, '<a href="#tab-content" class="active" data-tab="content">Content</a>')
113        self.assertContains(response, '<a href="#tab-promote" class="" data-tab="promote">Promote</a>')
114        # test register_page_action_menu_item hook
115        self.assertContains(response, '<button type="submit" name="action-panic" value="Panic!" class="button">Panic!</button>')
116        self.assertContains(response, 'testapp/js/siren.js')
117        # test construct_page_action_menu hook
118        self.assertContains(response, '<button type="submit" name="action-relax" value="Relax." class="button">Relax.</button>')
119        # test that workflow actions are shown
120        self.assertContains(
121            response, '<button type="submit" name="action-submit" value="Submit for moderation" class="button">'
122        )
123
124    @override_settings(WAGTAIL_WORKFLOW_ENABLED=False)
125    def test_workflow_buttons_not_shown_when_workflow_disabled(self):
126        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
127        self.assertEqual(response.status_code, 200)
128        self.assertNotContains(
129            response, 'value="Submit for moderation"'
130        )
131
132    def test_create_multipart(self):
133        """
134        Test checks if 'enctype="multipart/form-data"' is added and only to forms that require multipart encoding.
135        """
136        # check for SimplePage where is no file field
137        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
138        self.assertEqual(response.status_code, 200)
139        self.assertNotContains(response, 'enctype="multipart/form-data"')
140        self.assertTemplateUsed(response, 'wagtailadmin/pages/create.html')
141
142        # check for FilePage which has file field
143        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'filepage', self.root_page.id)))
144        self.assertEqual(response.status_code, 200)
145        self.assertContains(response, 'enctype="multipart/form-data"')
146
147    def test_create_page_without_promote_tab(self):
148        """
149        Test that the Promote tab is not rendered for page classes that define it as empty
150        """
151        response = self.client.get(
152            reverse('wagtailadmin_pages:add', args=('tests', 'standardindex', self.root_page.id))
153        )
154        self.assertEqual(response.status_code, 200)
155        self.assertContains(response, '<a href="#tab-content" class="active" data-tab="content">Content</a>')
156        self.assertNotContains(response, '<a href="#tab-promote" class="" data-tab="promote">Promote</a>')
157
158    def test_create_page_with_custom_tabs(self):
159        """
160        Test that custom edit handlers are rendered
161        """
162        response = self.client.get(
163            reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.root_page.id))
164        )
165        self.assertEqual(response.status_code, 200)
166        self.assertContains(response, '<a href="#tab-content" class="active" data-tab="content">Content</a>')
167        self.assertContains(response, '<a href="#tab-promote" class="" data-tab="promote">Promote</a>')
168        self.assertContains(response, '<a href="#tab-dinosaurs" class="" data-tab="dinosaurs">Dinosaurs</a>')
169
170    def test_create_page_with_non_model_field(self):
171        """
172        Test that additional fields defined on the form rather than the model are accepted and rendered
173        """
174        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'formclassadditionalfieldpage', self.root_page.id)))
175        self.assertEqual(response.status_code, 200)
176        self.assertTemplateUsed(response, 'wagtailadmin/pages/create.html')
177        self.assertContains(response, "Enter SMS authentication code")
178
179    def test_create_simplepage_bad_permissions(self):
180        # Remove privileges from user
181        self.user.is_superuser = False
182        self.user.user_permissions.add(
183            Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
184        )
185        self.user.save()
186
187        # Get page
188        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id, )))
189
190        # Check that the user received a 403 response
191        self.assertEqual(response.status_code, 302)
192
193    def test_cannot_create_page_with_is_creatable_false(self):
194        # tests.MTIBasePage has is_creatable=False, so attempting to add a new one
195        # should fail with permission denied
196        response = self.client.get(
197            reverse('wagtailadmin_pages:add', args=('tests', 'mtibasepage', self.root_page.id))
198        )
199        self.assertRedirects(response, '/admin/')
200
201    def test_cannot_create_page_when_can_create_at_returns_false(self):
202        # issue #2892
203
204        # Check that creating a second SingletonPage results in a permission
205        # denied error.
206
207        # SingletonPage overrides the can_create_at method to make it return
208        # False if another SingletonPage already exists.
209
210        # The Page model now has a max_count attribute (issue 4841),
211        # but we are leaving this test in place to cover existing behaviour and
212        # ensure it does not break any code doing this in the wild.
213        add_url = reverse('wagtailadmin_pages:add', args=[
214            SingletonPage._meta.app_label, SingletonPage._meta.model_name, self.root_page.pk])
215
216        # A single singleton page should be creatable
217        self.assertTrue(SingletonPage.can_create_at(self.root_page))
218        response = self.client.get(add_url)
219        self.assertEqual(response.status_code, 200)
220
221        # Create a singleton page
222        self.root_page.add_child(instance=SingletonPage(
223            title='singleton', slug='singleton'))
224
225        # A second singleton page should not be creatable
226        self.assertFalse(SingletonPage.can_create_at(self.root_page))
227        response = self.client.get(add_url)
228        self.assertRedirects(response, '/admin/')
229
230    def test_cannot_create_singleton_page_with_max_count(self):
231        # Check that creating a second SingletonPageViaMaxCount results in a permission
232        # denied error.
233
234        # SingletonPageViaMaxCount uses the max_count attribute to limit the number of
235        # instance it can have.
236
237        add_url = reverse('wagtailadmin_pages:add', args=[
238            SingletonPageViaMaxCount._meta.app_label, SingletonPageViaMaxCount._meta.model_name, self.root_page.pk])
239
240        # A single singleton page should be creatable
241        self.assertTrue(SingletonPageViaMaxCount.can_create_at(self.root_page))
242        response = self.client.get(add_url)
243        self.assertEqual(response.status_code, 200)
244
245        # Create a singleton page
246        self.root_page.add_child(instance=SingletonPageViaMaxCount(
247            title='singleton', slug='singleton'))
248
249        # A second singleton page should not be creatable
250        self.assertFalse(SingletonPageViaMaxCount.can_create_at(self.root_page))
251        response = self.client.get(add_url)
252        self.assertRedirects(response, '/admin/')
253
254    def test_cannot_create_page_with_wrong_parent_page_types(self):
255        # tests.BusinessChild has limited parent_page_types, so attempting to add
256        # a new one at the root level should fail with permission denied
257        response = self.client.get(
258            reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.root_page.id))
259        )
260        self.assertRedirects(response, '/admin/')
261
262    def test_cannot_create_page_with_wrong_subpage_types(self):
263        # Add a BusinessIndex to test business rules in
264        business_index = BusinessIndex(
265            title="Hello world!",
266            slug="hello-world",
267        )
268        self.root_page.add_child(instance=business_index)
269
270        # BusinessIndex has limited subpage_types, so attempting to add a SimplePage
271        # underneath it should fail with permission denied
272        response = self.client.get(
273            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', business_index.id))
274        )
275        self.assertRedirects(response, '/admin/')
276
277    def test_create_simplepage_post(self):
278        post_data = {
279            'title': "New page!",
280            'content': "Some content",
281            'slug': 'hello-world',
282        }
283        response = self.client.post(
284            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)),
285            post_data
286        )
287
288        # Find the page and check it
289        page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
290
291        # Should be redirected to edit page
292        self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(page.id, )))
293
294        self.assertEqual(page.title, post_data['title'])
295        self.assertEqual(page.draft_title, post_data['title'])
296        self.assertIsInstance(page, SimplePage)
297        self.assertFalse(page.live)
298        self.assertFalse(page.first_published_at)
299
300        # treebeard should report no consistency problems with the tree
301        self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
302
303    def test_create_simplepage_scheduled(self):
304        go_live_at = timezone.now() + datetime.timedelta(days=1)
305        expire_at = timezone.now() + datetime.timedelta(days=2)
306        post_data = {
307            'title': "New page!",
308            'content': "Some content",
309            'slug': 'hello-world',
310            'go_live_at': submittable_timestamp(go_live_at),
311            'expire_at': submittable_timestamp(expire_at),
312        }
313        response = self.client.post(
314            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
315        )
316
317        # Should be redirected to explorer page
318        self.assertEqual(response.status_code, 302)
319
320        # Find the page and check the scheduled times
321        page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
322        self.assertEqual(page.go_live_at.date(), go_live_at.date())
323        self.assertEqual(page.expire_at.date(), expire_at.date())
324        self.assertEqual(page.expired, False)
325        self.assertTrue(page.status_string, "draft")
326
327        # No revisions with approved_go_live_at
328        self.assertFalse(PageRevision.objects.filter(page=page).exclude(approved_go_live_at__isnull=True).exists())
329
330    def test_create_simplepage_scheduled_go_live_before_expiry(self):
331        post_data = {
332            'title': "New page!",
333            'content': "Some content",
334            'slug': 'hello-world',
335            'go_live_at': submittable_timestamp(timezone.now() + datetime.timedelta(days=2)),
336            'expire_at': submittable_timestamp(timezone.now() + datetime.timedelta(days=1)),
337        }
338        response = self.client.post(
339            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
340        )
341
342        self.assertEqual(response.status_code, 200)
343
344        # Check that a form error was raised
345        self.assertFormError(response, 'form', 'go_live_at', "Go live date/time must be before expiry date/time")
346        self.assertFormError(response, 'form', 'expire_at', "Go live date/time must be before expiry date/time")
347
348        # form should be marked as having unsaved changes for the purposes of the dirty-forms warning
349        self.assertContains(response, "alwaysDirty: true")
350
351    def test_create_simplepage_scheduled_expire_in_the_past(self):
352        post_data = {
353            'title': "New page!",
354            'content': "Some content",
355            'slug': 'hello-world',
356            'expire_at': submittable_timestamp(timezone.now() + datetime.timedelta(days=-1)),
357        }
358        response = self.client.post(
359            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
360        )
361
362        self.assertEqual(response.status_code, 200)
363
364        # Check that a form error was raised
365        self.assertFormError(response, 'form', 'expire_at', "Expiry date/time must be in the future")
366
367        # form should be marked as having unsaved changes for the purposes of the dirty-forms warning
368        self.assertContains(response, "alwaysDirty: true")
369
370    def test_create_simplepage_post_publish(self):
371        # Connect a mock signal handler to page_published signal
372        mock_handler = mock.MagicMock()
373        page_published.connect(mock_handler)
374
375        # Post
376        post_data = {
377            'title': "New page!",
378            'content': "Some content",
379            'slug': 'hello-world',
380            'action-publish': "Publish",
381        }
382        response = self.client.post(
383            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
384        )
385
386        # Find the page and check it
387        page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
388
389        # Should be redirected to explorer
390        self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
391
392        self.assertEqual(page.title, post_data['title'])
393        self.assertEqual(page.draft_title, post_data['title'])
394        self.assertIsInstance(page, SimplePage)
395        self.assertTrue(page.live)
396        self.assertTrue(page.first_published_at)
397
398        # Check that the page_published signal was fired
399        self.assertEqual(mock_handler.call_count, 1)
400        mock_call = mock_handler.mock_calls[0][2]
401
402        self.assertEqual(mock_call['sender'], page.specific_class)
403        self.assertEqual(mock_call['instance'], page)
404        self.assertIsInstance(mock_call['instance'], page.specific_class)
405
406        # treebeard should report no consistency problems with the tree
407        self.assertFalse(any(Page.find_problems()), 'treebeard found consistency problems')
408
409    def test_create_simplepage_post_publish_scheduled(self):
410        go_live_at = timezone.now() + datetime.timedelta(days=1)
411        expire_at = timezone.now() + datetime.timedelta(days=2)
412        post_data = {
413            'title': "New page!",
414            'content': "Some content",
415            'slug': 'hello-world',
416            'action-publish': "Publish",
417            'go_live_at': submittable_timestamp(go_live_at),
418            'expire_at': submittable_timestamp(expire_at),
419        }
420        response = self.client.post(
421            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
422        )
423
424        # Should be redirected to explorer page
425        self.assertEqual(response.status_code, 302)
426
427        # Find the page and check it
428        page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
429        self.assertEqual(page.go_live_at.date(), go_live_at.date())
430        self.assertEqual(page.expire_at.date(), expire_at.date())
431        self.assertEqual(page.expired, False)
432
433        # A revision with approved_go_live_at should exist now
434        self.assertTrue(PageRevision.objects.filter(page=page).exclude(approved_go_live_at__isnull=True).exists())
435        # But Page won't be live
436        self.assertFalse(page.live)
437        self.assertFalse(page.first_published_at)
438        self.assertTrue(page.status_string, "scheduled")
439
440    def test_create_simplepage_post_submit(self):
441        # Create a moderator user for testing email
442        self.create_superuser('moderator', 'moderator@email.com', 'password')
443
444        # Submit
445        post_data = {
446            'title': "New page!",
447            'content': "Some content",
448            'slug': 'hello-world',
449            'action-submit': "Submit",
450        }
451        response = self.client.post(
452            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
453        )
454
455        # Find the page and check it
456        page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
457
458        # Should be redirected to explorer
459        self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
460
461        self.assertEqual(page.title, post_data['title'])
462        self.assertIsInstance(page, SimplePage)
463        self.assertFalse(page.live)
464        self.assertFalse(page.first_published_at)
465
466        # The page should now be in moderation
467        self.assertEqual(page.current_workflow_state.status, page.current_workflow_state.STATUS_IN_PROGRESS)
468
469    def test_create_simplepage_post_existing_slug(self):
470        # This tests the existing slug checking on page save
471
472        # Create a page
473        self.child_page = SimplePage(title="Hello world!", slug="hello-world", content="hello")
474        self.root_page.add_child(instance=self.child_page)
475
476        # Attempt to create a new one with the same slug
477        post_data = {
478            'title': "New page!",
479            'content': "Some content",
480            'slug': 'hello-world',
481            'action-publish': "Publish",
482        }
483        response = self.client.post(
484            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
485        )
486
487        # Should not be redirected (as the save should fail)
488        self.assertEqual(response.status_code, 200)
489
490        # Check that a form error was raised
491        self.assertFormError(response, 'form', 'slug', "This slug is already in use")
492
493        # form should be marked as having unsaved changes for the purposes of the dirty-forms warning
494        self.assertContains(response, "alwaysDirty: true")
495
496    def test_create_nonexistantparent(self):
497        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', 100000)))
498        self.assertEqual(response.status_code, 404)
499
500    def test_create_nonpagetype(self):
501        response = self.client.get(
502            reverse('wagtailadmin_pages:add', args=('wagtailimages', 'image', self.root_page.id))
503        )
504        self.assertEqual(response.status_code, 404)
505
506    def test_preview_on_create(self):
507        post_data = {
508            'title': "New page!",
509            'content': "Some content",
510            'slug': 'hello-world',
511            'action-submit': "Submit",
512        }
513        preview_url = reverse('wagtailadmin_pages:preview_on_add',
514                              args=('tests', 'simplepage', self.root_page.id))
515        response = self.client.post(preview_url, post_data)
516
517        # Check the JSON response
518        self.assertEqual(response.status_code, 200)
519        self.assertJSONEqual(response.content.decode(), {'is_valid': True})
520
521        response = self.client.get(preview_url)
522
523        # Check the HTML response
524        self.assertEqual(response.status_code, 200)
525        self.assertTemplateUsed(response, 'tests/simple_page.html')
526        self.assertContains(response, "New page!")
527
528        # Check that the treebeard attributes were set correctly on the page object
529        self.assertEqual(response.context['self'].depth, self.root_page.depth + 1)
530        self.assertTrue(response.context['self'].path.startswith(self.root_page.path))
531        self.assertEqual(response.context['self'].get_parent(), self.root_page)
532
533    def test_whitespace_titles(self):
534        post_data = {
535            'title': " ",  # Single space on purpose
536            'content': "Some content",
537            'slug': 'hello-world',
538            'action-submit': "Submit",
539        }
540        response = self.client.post(
541            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
542        )
543
544        # Check that a form error was raised
545        self.assertFormError(response, 'form', 'title', "This field is required.")
546
547    def test_whitespace_titles_with_tab(self):
548        post_data = {
549            'title': "\t",  # Single space on purpose
550            'content': "Some content",
551            'slug': 'hello-world',
552            'action-submit': "Submit",
553        }
554        response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
555
556        # Check that a form error was raised
557        self.assertFormError(response, 'form', 'title', "This field is required.")
558
559    def test_whitespace_titles_with_tab_in_seo_title(self):
560        post_data = {
561            'title': "Hello",
562            'content': "Some content",
563            'slug': 'hello-world',
564            'action-submit': "Submit",
565            'seo_title': '\t'
566        }
567        response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
568
569        # Should be successful, as seo_title is not required
570        self.assertEqual(response.status_code, 302)
571
572        # The tab should be automatically stripped from the seo_title
573        page = Page.objects.order_by('-id').first()
574        self.assertEqual(page.seo_title, '')
575
576    def test_whitespace_is_stripped_from_titles(self):
577        post_data = {
578            'title': "   Hello   ",
579            'content': "Some content",
580            'slug': 'hello-world',
581            'action-submit': "Submit",
582            'seo_title': '   hello SEO   '
583        }
584        response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
585
586        # Should be successful, as both title and seo_title are non-empty after stripping
587        self.assertEqual(response.status_code, 302)
588
589        # Whitespace should be automatically stripped from title and seo_title
590        page = Page.objects.order_by('-id').first()
591        self.assertEqual(page.title, 'Hello')
592        self.assertEqual(page.draft_title, 'Hello')
593        self.assertEqual(page.seo_title, 'hello SEO')
594
595    def test_long_slug(self):
596        post_data = {
597            'title': "Hello world",
598            'content': "Some content",
599            'slug': 'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world-'
600                    'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world-'
601                    'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world-'
602                    'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world',
603            'action-submit': "Submit",
604        }
605        response = self.client.post(
606            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data
607        )
608
609        # Check that a form error was raised
610        self.assertEqual(response.status_code, 200)
611        self.assertFormError(response, 'form', 'slug', "Ensure this value has at most 255 characters (it has 287).")
612
613    def test_before_create_page_hook(self):
614        def hook_func(request, parent_page, page_class):
615            self.assertIsInstance(request, HttpRequest)
616            self.assertEqual(parent_page.id, self.root_page.id)
617            self.assertEqual(page_class, SimplePage)
618
619            return HttpResponse("Overridden!")
620
621        with self.register_hook('before_create_page', hook_func):
622            response = self.client.get(
623                reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id))
624            )
625
626        self.assertEqual(response.status_code, 200)
627        self.assertEqual(response.content, b"Overridden!")
628
629    def test_before_create_page_hook_post(self):
630        def hook_func(request, parent_page, page_class):
631            self.assertIsInstance(request, HttpRequest)
632            self.assertEqual(parent_page.id, self.root_page.id)
633            self.assertEqual(page_class, SimplePage)
634
635            return HttpResponse("Overridden!")
636
637        with self.register_hook('before_create_page', hook_func):
638            post_data = {
639                'title': "New page!",
640                'content': "Some content",
641                'slug': 'hello-world',
642            }
643            response = self.client.post(
644                reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)),
645                post_data
646            )
647
648        self.assertEqual(response.status_code, 200)
649        self.assertEqual(response.content, b"Overridden!")
650
651        # page should not be created
652        self.assertFalse(Page.objects.filter(title="New page!").exists())
653
654    def test_after_create_page_hook(self):
655        def hook_func(request, page):
656            self.assertIsInstance(request, HttpRequest)
657            self.assertIsInstance(page, SimplePage)
658
659            return HttpResponse("Overridden!")
660
661        with self.register_hook('after_create_page', hook_func):
662            post_data = {
663                'title': "New page!",
664                'content': "Some content",
665                'slug': 'hello-world',
666            }
667            response = self.client.post(
668                reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)),
669                post_data
670            )
671
672        self.assertEqual(response.status_code, 200)
673        self.assertEqual(response.content, b"Overridden!")
674
675        # page should be created
676        self.assertTrue(Page.objects.filter(title="New page!").exists())
677
678    def test_after_publish_page(self):
679        def hook_func(request, page):
680            self.assertIsInstance(request, HttpRequest)
681            self.assertEqual(page.title, "New page!")
682
683            return HttpResponse("Overridden!")
684
685        with self.register_hook("after_publish_page", hook_func):
686            post_data = {
687                "title": "New page!",
688                "content": "Some content",
689                "slug": "hello-world",
690                "action-publish": "Publish",
691            }
692            response = self.client.post(
693                reverse("wagtailadmin_pages:add", args=("tests", "simplepage", self.root_page.id)),
694                post_data
695            )
696
697        self.assertEqual(response.status_code, 200)
698        self.assertEqual(response.content, b"Overridden!")
699        self.root_page.refresh_from_db()
700        self.assertEqual(self.root_page.get_children()[0].status_string, _("live"))
701
702    def test_before_publish_page(self):
703        def hook_func(request, page):
704            self.assertIsInstance(request, HttpRequest)
705            self.assertEqual(page.title, "New page!")
706
707            return HttpResponse("Overridden!")
708
709        with self.register_hook("before_publish_page", hook_func):
710            post_data = {
711                "title": "New page!",
712                "content": "Some content",
713                "slug": "hello-world",
714                "action-publish": "Publish",
715            }
716            response = self.client.post(
717                reverse("wagtailadmin_pages:add", args=("tests", "simplepage", self.root_page.id)),
718                post_data
719            )
720
721        self.assertEqual(response.status_code, 200)
722        self.assertEqual(response.content, b"Overridden!")
723        self.root_page.refresh_from_db()
724        self.assertEqual(self.root_page.get_children()[0].status_string, _("live + draft"))
725
726    def test_display_moderation_button_by_default(self):
727        """
728        Tests that by default the "Submit for Moderation" button is shown in the action menu.
729        """
730        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
731        self.assertContains(
732            response,
733            '<button type="submit" name="action-submit" value="Submit for moderation" class="button">'
734            '<svg class="icon icon-resubmit icon" aria-hidden="true" focusable="false"><use href="#icon-resubmit"></use></svg>'
735            'Submit for moderation</button>'
736        )
737
738    @override_settings(WAGTAIL_MODERATION_ENABLED=False)
739    def test_hide_moderation_button(self):
740        """
741        Tests that if WAGTAIL_MODERATION_ENABLED is set to False, the "Submit for Moderation" button is not shown.
742        """
743        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
744        self.assertNotContains(response, '<button type="submit" name="action-submit" value="Submit for moderation" class="button">Submit for moderation</button>')
745
746    def test_create_sets_locale_to_parent_locale(self):
747        # We need to make sure the page's locale it set to the parent in the create view so that any customisations
748        # for that language will take effect.
749        fr_locale = Locale.objects.create(language_code="fr")
750        fr_homepage = self.root_page.add_child(instance=Page(
751            title="Home",
752            slug="home-fr",
753            locale=fr_locale,
754        ))
755
756        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', fr_homepage.id)))
757
758        self.assertEqual(response.context['page'].locale, fr_locale)
759
760
761class TestPerRequestEditHandler(TestCase, WagtailTestUtils):
762    fixtures = ['test.json']
763
764    def setUp(self):
765        # Find root page
766        self.root_page = Page.objects.get(id=2)
767        GroupPagePermission.objects.create(
768            group=Group.objects.get(name="Site-wide editors"),
769            page=self.root_page, permission_type='add'
770        )
771
772    def test_create_page_with_per_request_custom_edit_handlers(self):
773        """
774        Test that per-request custom behaviour in edit handlers is honoured
775        """
776        # non-superusers should not see secret_data
777        self.login(username='siteeditor', password='password')
778        response = self.client.get(
779            reverse('wagtailadmin_pages:add', args=('tests', 'secretpage', self.root_page.id))
780        )
781        self.assertEqual(response.status_code, 200)
782        self.assertContains(response, '"boring_data"')
783        self.assertNotContains(response, '"secret_data"')
784
785        # superusers should see secret_data
786        self.login(username='superuser', password='password')
787        response = self.client.get(
788            reverse('wagtailadmin_pages:add', args=('tests', 'secretpage', self.root_page.id))
789        )
790        self.assertEqual(response.status_code, 200)
791        self.assertContains(response, '"boring_data"')
792        self.assertContains(response, '"secret_data"')
793
794
795class TestSubpageBusinessRules(TestCase, WagtailTestUtils):
796    def setUp(self):
797        # Find root page
798        self.root_page = Page.objects.get(id=2)
799
800        # Add standard page (allows subpages of any type)
801        self.standard_index = StandardIndex()
802        self.standard_index.title = "Standard Index"
803        self.standard_index.slug = "standard-index"
804        self.root_page.add_child(instance=self.standard_index)
805
806        # Add business page (allows BusinessChild and BusinessSubIndex as subpages)
807        self.business_index = BusinessIndex()
808        self.business_index.title = "Business Index"
809        self.business_index.slug = "business-index"
810        self.root_page.add_child(instance=self.business_index)
811
812        # Add business child (allows no subpages)
813        self.business_child = BusinessChild()
814        self.business_child.title = "Business Child"
815        self.business_child.slug = "business-child"
816        self.business_index.add_child(instance=self.business_child)
817
818        # Add business subindex (allows only BusinessChild as subpages)
819        self.business_subindex = BusinessSubIndex()
820        self.business_subindex.title = "Business Subindex"
821        self.business_subindex.slug = "business-subindex"
822        self.business_index.add_child(instance=self.business_subindex)
823
824        # Login
825        self.login()
826
827    def test_standard_subpage(self):
828        add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.standard_index.id, ))
829
830        # explorer should contain a link to 'add child page'
831        response = self.client.get(reverse('wagtailadmin_explore', args=(self.standard_index.id, )))
832        self.assertEqual(response.status_code, 200)
833        self.assertContains(response, add_subpage_url)
834
835        # add_subpage should give us choices of StandardChild, and BusinessIndex.
836        # BusinessSubIndex and BusinessChild are not allowed
837        response = self.client.get(add_subpage_url)
838        self.assertEqual(response.status_code, 200)
839        self.assertContains(response, StandardChild.get_verbose_name())
840        self.assertContains(response, BusinessIndex.get_verbose_name())
841        self.assertNotContains(response, BusinessSubIndex.get_verbose_name())
842        self.assertNotContains(response, BusinessChild.get_verbose_name())
843
844    def test_business_subpage(self):
845        add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.business_index.id, ))
846
847        # explorer should contain a link to 'add child page'
848        response = self.client.get(reverse('wagtailadmin_explore', args=(self.business_index.id, )))
849        self.assertEqual(response.status_code, 200)
850        self.assertContains(response, add_subpage_url)
851
852        # add_subpage should give us a cut-down set of page types to choose
853        response = self.client.get(add_subpage_url)
854        self.assertEqual(response.status_code, 200)
855        self.assertNotContains(response, StandardIndex.get_verbose_name())
856        self.assertNotContains(response, StandardChild.get_verbose_name())
857        self.assertContains(response, BusinessSubIndex.get_verbose_name())
858        self.assertContains(response, BusinessChild.get_verbose_name())
859
860    def test_business_child_subpage(self):
861        add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.business_child.id, ))
862
863        # explorer should not contain a link to 'add child page', as this page doesn't accept subpages
864        response = self.client.get(reverse('wagtailadmin_explore', args=(self.business_child.id, )))
865        self.assertEqual(response.status_code, 200)
866        self.assertNotContains(response, add_subpage_url)
867
868        # this also means that fetching add_subpage is blocked at the permission-check level
869        response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.business_child.id, )))
870        self.assertEqual(response.status_code, 302)
871
872    def test_cannot_add_invalid_subpage_type(self):
873        # cannot add StandardChild as a child of BusinessIndex, as StandardChild is not present in subpage_types
874        response = self.client.get(
875            reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.business_index.id))
876        )
877        self.assertRedirects(response, '/admin/')
878
879        # likewise for BusinessChild which has an empty subpage_types list
880        response = self.client.get(
881            reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.business_child.id))
882        )
883        self.assertRedirects(response, '/admin/')
884
885        # cannot add BusinessChild to StandardIndex, as BusinessChild restricts is parent page types
886        response = self.client.get(
887            reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.standard_index.id))
888        )
889        self.assertRedirects(response, '/admin/')
890
891        # but we can add a BusinessChild to BusinessIndex
892        response = self.client.get(
893            reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.business_index.id))
894        )
895        self.assertEqual(response.status_code, 200)
896
897    def test_not_prompted_for_page_type_when_only_one_choice(self):
898        response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.business_subindex.id, )))
899        # BusinessChild is the only valid subpage type of BusinessSubIndex, so redirect straight there
900        self.assertRedirects(
901            response, reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.business_subindex.id))
902        )
903
904
905class TestInlinePanelMedia(TestCase, WagtailTestUtils):
906    """
907    Test that form media required by InlinePanels is correctly pulled in to the edit page
908    """
909
910    def test_inline_panel_media(self):
911        homepage = Page.objects.get(id=2)
912        self.login()
913
914        # simplepage does not need draftail...
915        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', homepage.id)))
916        self.assertEqual(response.status_code, 200)
917        self.assertNotContains(response, 'wagtailadmin/js/draftail.js')
918
919        # but sectionedrichtextpage does
920        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'sectionedrichtextpage', homepage.id)))
921        self.assertEqual(response.status_code, 200)
922        self.assertContains(response, 'wagtailadmin/js/draftail.js')
923
924
925class TestInlineStreamField(TestCase, WagtailTestUtils):
926    """
927    Test that streamfields inside an inline child work
928    """
929
930    def test_inline_streamfield(self):
931        homepage = Page.objects.get(id=2)
932        self.login()
933
934        response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'inlinestreampage', homepage.id)))
935        self.assertEqual(response.status_code, 200)
936
937        # response should include HTML declarations for streamfield child blocks
938        self.assertContains(response, '<div id="sections-__prefix__-body" data-block="')
939
940
941class TestIssue2994(TestCase, WagtailTestUtils):
942    """
943    In contrast to most "standard" form fields, StreamField form widgets generally won't
944    provide a postdata field with a name exactly matching the field name. To prevent Django
945    from wrongly interpreting this as the field being omitted from the form,
946    we need to provide a custom value_omitted_from_data method.
947    """
948
949    def setUp(self):
950        self.root_page = Page.objects.get(id=2)
951        self.user = self.login()
952
953    def test_page_edit_post_publish_url(self):
954        # Post
955        post_data = {
956            'title': "Issue 2994 test",
957            'slug': 'issue-2994-test',
958            'body-count': '1',
959            'body-0-deleted': '',
960            'body-0-order': '0',
961            'body-0-type': 'text',
962            'body-0-value': 'hello world',
963            'action-publish': "Publish",
964        }
965        self.client.post(
966            reverse('wagtailadmin_pages:add', args=('tests', 'defaultstreampage', self.root_page.id)), post_data
967        )
968        new_page = DefaultStreamPage.objects.get(slug='issue-2994-test')
969        self.assertEqual(1, len(new_page.body))
970        self.assertEqual('hello world', new_page.body[0].value)
971
972
973class TestInlinePanelWithTags(TestCase, WagtailTestUtils):
974    # https://github.com/wagtail/wagtail/issues/5414#issuecomment-567080707
975
976    def setUp(self):
977        self.root_page = Page.objects.get(id=2)
978        self.user = self.login()
979
980    def test_create(self):
981        post_data = {
982            'title': 'Mr Benn',
983            'slug': 'mr-benn',
984            'first_name': 'William',
985            'last_name': 'Benn',
986            'addresses-TOTAL_FORMS': 1,
987            'addresses-INITIAL_FORMS': 0,
988            'addresses-MIN_NUM_FORMS': 0,
989            'addresses-MAX_NUM_FORMS': 1000,
990            'addresses-0-address': "52 Festive Road, London",
991            'addresses-0-tags': "shopkeeper, bowler-hat",
992            'action-publish': "Publish",
993            'comments-TOTAL_FORMS': 0,
994            'comments-INITIAL_FORMS': 0,
995            'comments-MIN_NUM_FORMS': 0,
996            'comments-MAX_NUM_FORMS': 1000,
997        }
998        response = self.client.post(
999            reverse('wagtailadmin_pages:add', args=('tests', 'personpage', self.root_page.id)), post_data
1000        )
1001        self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
1002        new_page = PersonPage.objects.get(slug='mr-benn')
1003        self.assertEqual(new_page.addresses.first().tags.count(), 2)
1004
1005
1006class TestInlinePanelNonFieldErrors(TestCase, WagtailTestUtils):
1007    """
1008    Test that non field errors will render for InlinePanels
1009    https://github.com/wagtail/wagtail/issues/3890
1010    """
1011    fixtures = ['demosite.json']
1012
1013    def setUp(self):
1014        self.root_page = Page.objects.get(id=2)
1015        self.user = self.login()
1016
1017    def test_create(self):
1018        post_data = {
1019            'title': 'Issue 3890 test',
1020            'slug': 'issue-3890-test',
1021            'related_links-TOTAL_FORMS': 1,
1022            'related_links-INITIAL_FORMS': 0,
1023            'related_links-MIN_NUM_FORMS': 0,
1024            'related_links-MAX_NUM_FORMS': 1000,
1025            'related_links-0-id': 0,
1026            'related_links-0-ORDER': 1,
1027
1028            # Leaving all fields empty should raise a validation error
1029            'related_links-0-link_page': "",
1030            'related_links-0-link_document': "",
1031            'related_links-0-link_external': "",
1032            'carousel_items-INITIAL_FORMS': 0,
1033            'carousel_items-MAX_NUM_FORMS': 1000,
1034            'carousel_items-TOTAL_FORMS': 0,
1035            'action-publish': "Publish",
1036            'comments-TOTAL_FORMS': 0,
1037            'comments-INITIAL_FORMS': 0,
1038            'comments-MIN_NUM_FORMS': 0,
1039            'comments-MAX_NUM_FORMS': 1000,
1040        }
1041        response = self.client.post(
1042            reverse('wagtailadmin_pages:add', args=('demosite', 'homepage', self.root_page.id)), post_data
1043        )
1044        self.assertEqual(response.status_code, 200)
1045        self.assertContains(response, "The page could not be created due to validation errors")
1046        self.assertContains(response, 'You must provide a related page, related document or an external URL', count=1)
1047
1048
1049@override_settings(WAGTAIL_I18N_ENABLED=True)
1050class TestLocaleSelector(TestCase, WagtailTestUtils):
1051    fixtures = ['test.json']
1052
1053    def setUp(self):
1054        self.events_page = Page.objects.get(url_path='/home/events/')
1055        self.fr_locale = Locale.objects.create(language_code='fr')
1056        self.translated_events_page = self.events_page.copy_for_translation(self.fr_locale, copy_parents=True)
1057        self.user = self.login()
1058
1059    def test_locale_selector(self):
1060        response = self.client.get(
1061            reverse('wagtailadmin_pages:add', args=['tests', 'eventpage', self.events_page.id])
1062        )
1063
1064        self.assertContains(response, '<li class="header-meta--locale">')
1065
1066        add_translation_url = reverse('wagtailadmin_pages:add', args=['tests', 'eventpage', self.translated_events_page.id])
1067        self.assertContains(response, f'<a href="{add_translation_url}" aria-label="French" class="u-link is-live">')
1068
1069    @override_settings(WAGTAIL_I18N_ENABLED=False)
1070    def test_locale_selector_not_present_when_i18n_disabled(self):
1071        response = self.client.get(
1072            reverse('wagtailadmin_pages:add', args=['tests', 'eventpage', self.events_page.id])
1073        )
1074
1075        self.assertNotContains(response, '<li class="header-meta--locale">')
1076
1077        add_translation_url = reverse('wagtailadmin_pages:add', args=['tests', 'eventpage', self.translated_events_page.id])
1078        self.assertNotContains(response, f'<a href="{add_translation_url}" aria-label="French" class="u-link is-live">')
1079
1080    def test_locale_dropdown_not_present_without_permission_to_add(self):
1081        # Remove user's permissions to add in the French tree
1082        group = Group.objects.get(name='Moderators')
1083        GroupPagePermission.objects.create(
1084            group=group,
1085            page=self.events_page,
1086            permission_type='add',
1087        )
1088        self.user.is_superuser = False
1089        self.user.user_permissions.add(
1090            Permission.objects.get(content_type__app_label='wagtailadmin', codename='access_admin')
1091        )
1092        self.user.groups.add(group)
1093        self.user.save()
1094
1095        # Locale indicator should exist, but the "French" option should be hidden
1096        response = self.client.get(
1097            reverse('wagtailadmin_pages:add', args=['tests', 'eventpage', self.events_page.id])
1098        )
1099
1100        self.assertContains(response, '<li class="header-meta--locale">')
1101
1102        add_translation_url = reverse('wagtailadmin_pages:add', args=['tests', 'eventpage', self.translated_events_page.id])
1103        self.assertNotContains(response, f'<a href="{add_translation_url}" aria-label="French" class="u-link is-live">')
1104
1105
1106@override_settings(WAGTAIL_I18N_ENABLED=True)
1107class TestLocaleSelectorOnRootPage(TestCase, WagtailTestUtils):
1108    fixtures = ['test.json']
1109
1110    def setUp(self):
1111        self.root_page = Page.objects.get(id=1)
1112        self.fr_locale = Locale.objects.create(language_code='fr')
1113        self.user = self.login()
1114
1115    def test_locale_selector(self):
1116        response = self.client.get(
1117            reverse('wagtailadmin_pages:add', args=['demosite', 'homepage', self.root_page.id])
1118        )
1119
1120        self.assertContains(response, '<li class="header-meta--locale">')
1121
1122        add_translation_url = reverse('wagtailadmin_pages:add', args=['demosite', 'homepage', self.root_page.id]) + '?locale=fr'
1123        self.assertContains(response, f'<a href="{add_translation_url}" aria-label="French" class="u-link is-live">')
1124
1125    @override_settings(WAGTAIL_I18N_ENABLED=False)
1126    def test_locale_selector_not_present_when_i18n_disabled(self):
1127        response = self.client.get(
1128            reverse('wagtailadmin_pages:add', args=['demosite', 'homepage', self.root_page.id])
1129        )
1130
1131        self.assertNotContains(response, '<li class="header-meta--locale">')
1132
1133        add_translation_url = reverse('wagtailadmin_pages:add', args=['demosite', 'homepage', self.root_page.id]) + '?locale=fr'
1134        self.assertNotContains(response, f'<a href="{add_translation_url}" aria-label="French" class="u-link is-live">')
1135
1136
1137class TestPageSubscriptionSettings(TestCase, WagtailTestUtils):
1138    def setUp(self):
1139        # Find root page
1140        self.root_page = Page.objects.get(id=2)
1141
1142        # Login
1143        self.user = self.login()
1144
1145    def test_commment_notifications_switched_on_by_default(self):
1146        response = self.client.get(reverse('wagtailadmin_pages:add', args=['tests', 'simplepage', self.root_page.id]))
1147
1148        self.assertEqual(response.status_code, 200)
1149        self.assertContains(response, '<input type="checkbox" name="comment_notifications" id="id_comment_notifications" checked>')
1150
1151    def test_post_with_comment_notifications_switched_on(self):
1152        post_data = {
1153            'title': "New page!",
1154            'content': "Some content",
1155            'slug': 'hello-world',
1156            'comment_notifications': 'on'
1157        }
1158        response = self.client.post(reverse('wagtailadmin_pages:add', args=['tests', 'simplepage', self.root_page.id]), post_data)
1159
1160        page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
1161
1162        self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=[page.id]))
1163
1164        # Check the subscription
1165        subscription = page.subscribers.get()
1166
1167        self.assertEqual(subscription.user, self.user)
1168        self.assertTrue(subscription.comment_notifications)
1169
1170    def test_post_with_comment_notifications_switched_off(self):
1171        post_data = {
1172            'title': "New page!",
1173            'content': "Some content",
1174            'slug': 'hello-world',
1175        }
1176        response = self.client.post(reverse('wagtailadmin_pages:add', args=['tests', 'simplepage', self.root_page.id]), post_data)
1177
1178        page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
1179
1180        self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=[page.id]))
1181
1182        # Check the subscription
1183        subscription = page.subscribers.get()
1184
1185        self.assertEqual(subscription.user, self.user)
1186        self.assertFalse(subscription.comment_notifications)
1187