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