123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422 |
- import datetime
- import json
- import pytz
- from django.contrib.auth import get_user_model
- from django.contrib.auth.models import AnonymousUser
- from django.contrib.contenttypes.models import ContentType
- from django.core.exceptions import ValidationError
- from django.http import Http404, HttpRequest
- from django.test import Client, TestCase
- from django.test.client import RequestFactory
- from django.test.utils import override_settings
- from freezegun import freeze_time
- from wagtail.core.models import Page, PageManager, Site, get_page_models
- from wagtail.tests.testapp.models import (
- AbstractPage, Advert, AlwaysShowInMenusPage, BlogCategory, BlogCategoryBlogPage, BusinessChild,
- BusinessIndex, BusinessNowherePage, BusinessSubIndex, CustomManager, CustomManagerPage,
- CustomPageQuerySet, EventIndex, EventPage, GenericSnippetPage, ManyToManyBlogPage, MTIBasePage,
- MTIChildPage, MyCustomPage, OneToOnePage, PageWithExcludedCopyField, SimplePage,
- SingleEventPage, SingletonPage, StandardIndex, TaggedPage)
- from wagtail.tests.utils import WagtailTestUtils
- def get_ct(model):
- return ContentType.objects.get_for_model(model)
- class TestValidation(TestCase):
- fixtures = ['test.json']
- def test_can_create(self):
- """
- Check that basic page creation works
- """
- homepage = Page.objects.get(url_path='/home/')
- hello_page = SimplePage(title="Hello world", slug='hello-world', content="hello")
- homepage.add_child(instance=hello_page)
- # check that hello_page exists in the db
- retrieved_page = Page.objects.get(id=hello_page.id)
- self.assertEqual(retrieved_page.title, "Hello world")
- def test_title_is_required(self):
- homepage = Page.objects.get(url_path='/home/')
- hello_page = SimplePage(slug='hello-world', content="hello")
- with self.assertRaises(ValidationError):
- homepage.add_child(instance=hello_page)
- hello_page = SimplePage(title="", slug='hello-world', content="hello")
- with self.assertRaises(ValidationError):
- homepage.add_child(instance=hello_page)
- def test_slug_is_autogenerated(self):
- homepage = Page.objects.get(url_path='/home/')
- # slug should be auto-assigned to a slugified version of the title
- hello_page = SimplePage(title="Hello world", content="hello")
- homepage.add_child(instance=hello_page)
- retrieved_page = Page.objects.get(id=hello_page.id)
- self.assertEqual(retrieved_page.slug, 'hello-world')
- # auto-generated slug should receive a suffix to make it unique
- events_page = SimplePage(title="Events", content="hello")
- homepage.add_child(instance=events_page)
- retrieved_page = Page.objects.get(id=events_page.id)
- self.assertEqual(retrieved_page.slug, 'events-2')
- def test_slug_must_be_unique_within_parent(self):
- homepage = Page.objects.get(url_path='/home/')
- events_page = SimplePage(title="Events", slug='events', content="hello")
- with self.assertRaises(ValidationError):
- homepage.add_child(instance=events_page)
- def test_slug_can_duplicate_other_sections(self):
- homepage = Page.objects.get(url_path='/home/')
- # the Events section has a page with slug='christmas', but we still allow
- # it as a slug elsewhere
- christmas_page = SimplePage(title="Christmas", slug='christmas', content="hello")
- homepage.add_child(instance=christmas_page)
- self.assertTrue(Page.objects.filter(id=christmas_page.id).exists())
- def test_get_admin_display_title(self):
- homepage = Page.objects.get(url_path='/home/')
- self.assertEqual(homepage.draft_title, homepage.get_admin_display_title())
- def test_get_admin_display_title_with_blank_draft_title(self):
- # Display title should fall back on the live title if draft_title is blank;
- # this can happen if the page was created through fixtures or migrations that
- # didn't explicitly account for draft_title
- # (since draft_title doesn't get populated automatically on save in those cases)
- Page.objects.filter(url_path='/home/').update(title='live title', draft_title='')
- homepage = Page.objects.get(url_path='/home/')
- self.assertEqual(homepage.get_admin_display_title(), 'live title')
- def test_draft_title_is_autopopulated(self):
- homepage = Page.objects.get(url_path='/home/')
- hello_page = SimplePage(title="Hello world", content="hello")
- homepage.add_child(instance=hello_page)
- retrieved_page = Page.objects.get(id=hello_page.id)
- self.assertEqual(retrieved_page.draft_title, "Hello world")
- hello_page = SimplePage(title="Hello world", draft_title="Hello world edited", content="hello")
- homepage.add_child(instance=hello_page)
- retrieved_page = Page.objects.get(id=hello_page.id)
- self.assertEqual(retrieved_page.draft_title, "Hello world edited")
- @override_settings(ALLOWED_HOSTS=['localhost', 'events.example.com', 'about.example.com', 'unknown.site.com'])
- class TestSiteRouting(TestCase):
- fixtures = ['test.json']
- def setUp(self):
- self.default_site = Site.objects.get(is_default_site=True)
- events_page = Page.objects.get(url_path='/home/events/')
- about_page = Page.objects.get(url_path='/home/about-us/')
- self.events_site = Site.objects.create(hostname='events.example.com', root_page=events_page)
- self.alternate_port_events_site = Site.objects.create(
- hostname='events.example.com',
- root_page=events_page,
- port='8765'
- )
- self.about_site = Site.objects.create(hostname='about.example.com', root_page=about_page)
- self.alternate_port_default_site = Site.objects.create(hostname=self.default_site.hostname, port='8765', root_page=self.default_site.root_page)
- self.unrecognised_port = '8000'
- self.unrecognised_hostname = 'unknown.site.com'
- def test_no_host_header_routes_to_default_site(self):
- # requests without a Host: header should be directed to the default site
- request = HttpRequest()
- request.path = '/'
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.default_site)
- def test_valid_headers_route_to_specific_site(self):
- # requests with a known Host: header should be directed to the specific site
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = self.events_site.hostname
- request.META['SERVER_PORT'] = self.events_site.port
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.events_site)
- def test_ports_in_request_headers_are_respected(self):
- # ports in the Host: header should be respected
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = self.alternate_port_events_site.hostname
- request.META['SERVER_PORT'] = self.alternate_port_events_site.port
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.alternate_port_events_site)
- def test_unrecognised_host_header_routes_to_default_site(self):
- # requests with an unrecognised Host: header should be directed to the default site
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = self.unrecognised_hostname
- request.META['SERVER_PORT'] = '80'
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.default_site)
- def test_unrecognised_port_and_default_host_routes_to_default_site(self):
- # requests to the default host on an unrecognised port should be directed to the default site
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = self.default_site.hostname
- request.META['SERVER_PORT'] = self.unrecognised_port
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.default_site)
- def test_unrecognised_port_and_unrecognised_host_routes_to_default_site(self):
- # requests with an unrecognised Host: header _and_ an unrecognised port
- # hould be directed to the default site
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = self.unrecognised_hostname
- request.META['SERVER_PORT'] = self.unrecognised_port
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.default_site)
- def test_unrecognised_port_on_known_hostname_routes_there_if_no_ambiguity(self):
- # requests on an unrecognised port should be directed to the site with
- # matching hostname if there is no ambiguity
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = self.about_site.hostname
- request.META['SERVER_PORT'] = self.unrecognised_port
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.about_site)
- def test_unrecognised_port_on_known_hostname_routes_to_default_site_if_ambiguity(self):
- # requests on an unrecognised port should be directed to the default
- # site, even if their hostname (but not port) matches more than one
- # other entry
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = self.events_site.hostname
- request.META['SERVER_PORT'] = self.unrecognised_port
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.default_site)
- def test_port_in_http_host_header_is_ignored(self):
- # port in the HTTP_HOST header is ignored
- request = HttpRequest()
- request.path = '/'
- request.META['HTTP_HOST'] = "%s:%s" % (self.events_site.hostname, self.events_site.port)
- request.META['SERVER_PORT'] = self.alternate_port_events_site.port
- with self.assertNumQueries(1):
- self.assertEqual(Site.find_for_request(request), self.alternate_port_events_site)
- class TestRouting(TestCase):
- fixtures = ['test.json']
- # need to clear urlresolver caches before/after tests, because we override ROOT_URLCONF
- # in some tests here
- def setUp(self):
- from django.urls import clear_url_caches
- clear_url_caches()
- def tearDown(self):
- from django.urls import clear_url_caches
- clear_url_caches()
- def test_urls(self):
- default_site = Site.objects.get(is_default_site=True)
- homepage = Page.objects.get(url_path='/home/')
- christmas_page = Page.objects.get(url_path='/home/events/christmas/')
- # Basic installation only has one site configured, so page.url will return local URLs
- self.assertEqual(
- homepage.get_url_parts(),
- (default_site.id, 'http://localhost', '/')
- )
- self.assertEqual(homepage.full_url, 'http://localhost/')
- self.assertEqual(homepage.url, '/')
- self.assertEqual(homepage.relative_url(default_site), '/')
- self.assertEqual(homepage.get_site(), default_site)
- self.assertEqual(
- christmas_page.get_url_parts(),
- (default_site.id, 'http://localhost', '/events/christmas/')
- )
- self.assertEqual(christmas_page.full_url, 'http://localhost/events/christmas/')
- self.assertEqual(christmas_page.url, '/events/christmas/')
- self.assertEqual(christmas_page.relative_url(default_site), '/events/christmas/')
- self.assertEqual(christmas_page.get_site(), default_site)
- def test_page_with_no_url(self):
- root = Page.objects.get(url_path='/')
- default_site = Site.objects.get(is_default_site=True)
- self.assertEqual(root.get_url_parts(), None)
- self.assertEqual(root.full_url, None)
- self.assertEqual(root.url, None)
- self.assertEqual(root.relative_url(default_site), None)
- self.assertEqual(root.get_site(), None)
- def test_urls_with_multiple_sites(self):
- events_page = Page.objects.get(url_path='/home/events/')
- events_site = Site.objects.create(hostname='events.example.com', root_page=events_page)
- default_site = Site.objects.get(is_default_site=True)
- homepage = Page.objects.get(url_path='/home/')
- christmas_page = Page.objects.get(url_path='/home/events/christmas/')
- # with multiple sites, page.url will return full URLs to ensure that
- # they work across sites
- self.assertEqual(
- homepage.get_url_parts(),
- (default_site.id, 'http://localhost', '/')
- )
- self.assertEqual(homepage.full_url, 'http://localhost/')
- self.assertEqual(homepage.url, 'http://localhost/')
- self.assertEqual(homepage.relative_url(default_site), '/')
- self.assertEqual(homepage.relative_url(events_site), 'http://localhost/')
- self.assertEqual(homepage.get_site(), default_site)
- self.assertEqual(
- christmas_page.get_url_parts(),
- (events_site.id, 'http://events.example.com', '/christmas/')
- )
- self.assertEqual(christmas_page.full_url, 'http://events.example.com/christmas/')
- self.assertEqual(christmas_page.url, 'http://events.example.com/christmas/')
- self.assertEqual(christmas_page.relative_url(default_site), 'http://events.example.com/christmas/')
- self.assertEqual(christmas_page.relative_url(events_site), '/christmas/')
- self.assertEqual(christmas_page.get_site(), events_site)
- @override_settings(ROOT_URLCONF='wagtail.tests.non_root_urls')
- def test_urls_with_non_root_urlconf(self):
- default_site = Site.objects.get(is_default_site=True)
- homepage = Page.objects.get(url_path='/home/')
- christmas_page = Page.objects.get(url_path='/home/events/christmas/')
- # Basic installation only has one site configured, so page.url will return local URLs
- self.assertEqual(
- homepage.get_url_parts(),
- (default_site.id, 'http://localhost', '/site/')
- )
- self.assertEqual(homepage.full_url, 'http://localhost/site/')
- self.assertEqual(homepage.url, '/site/')
- self.assertEqual(homepage.relative_url(default_site), '/site/')
- self.assertEqual(homepage.get_site(), default_site)
- self.assertEqual(
- christmas_page.get_url_parts(),
- (default_site.id, 'http://localhost', '/site/events/christmas/')
- )
- self.assertEqual(christmas_page.full_url, 'http://localhost/site/events/christmas/')
- self.assertEqual(christmas_page.url, '/site/events/christmas/')
- self.assertEqual(christmas_page.relative_url(default_site), '/site/events/christmas/')
- self.assertEqual(christmas_page.get_site(), default_site)
- def test_request_routing(self):
- homepage = Page.objects.get(url_path='/home/')
- christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
- request = HttpRequest()
- request.path = '/events/christmas/'
- (found_page, args, kwargs) = homepage.route(request, ['events', 'christmas'])
- self.assertEqual(found_page, christmas_page)
- def test_request_serving(self):
- christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
- request = HttpRequest()
- request.user = AnonymousUser()
- request.site = Site.objects.first()
- response = christmas_page.serve(request)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context_data['self'], christmas_page)
- # confirm that the event_page.html template was used
- self.assertContains(response, '<h2>Event</h2>')
- def test_route_to_unknown_page_returns_404(self):
- homepage = Page.objects.get(url_path='/home/')
- request = HttpRequest()
- request.path = '/events/quinquagesima/'
- with self.assertRaises(Http404):
- homepage.route(request, ['events', 'quinquagesima'])
- def test_route_to_unpublished_page_returns_404(self):
- homepage = Page.objects.get(url_path='/home/')
- request = HttpRequest()
- request.path = '/events/tentative-unpublished-event/'
- with self.assertRaises(Http404):
- homepage.route(request, ['events', 'tentative-unpublished-event'])
- # Override CACHES so we don't generate any cache-related SQL queries (tests use DatabaseCache
- # otherwise) and so cache.get will always return None.
- @override_settings(CACHES={'default': {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}})
- def test_request_scope_site_root_paths_cache(self):
- homepage = Page.objects.get(url_path='/home/')
- christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
- # without a request, get_url should only issue 1 SQL query
- with self.assertNumQueries(1):
- self.assertEqual(homepage.get_url(), '/')
- # subsequent calls with the same page should generate no SQL queries
- with self.assertNumQueries(0):
- self.assertEqual(homepage.get_url(), '/')
- # subsequent calls with a different page will still generate 1 SQL query
- with self.assertNumQueries(1):
- self.assertEqual(christmas_page.get_url(), '/events/christmas/')
- # with a request, the first call to get_url should issue 1 SQL query
- request = HttpRequest()
- with self.assertNumQueries(1):
- self.assertEqual(homepage.get_url(request=request), '/')
- # subsequent calls should issue no SQL queries
- with self.assertNumQueries(0):
- self.assertEqual(homepage.get_url(request=request), '/')
- # even if called on a different page
- with self.assertNumQueries(0):
- self.assertEqual(christmas_page.get_url(request=request), '/events/christmas/')
- class TestServeView(TestCase):
- fixtures = ['test.json']
- def setUp(self):
- # Explicitly clear the cache of site root paths. Normally this would be kept
- # in sync by the Site.save logic, but this is bypassed when the database is
- # rolled back between tests using transactions.
- from django.core.cache import cache
- cache.delete('wagtail_site_root_paths')
- # also need to clear urlresolver caches before/after tests, because we override
- # ROOT_URLCONF in some tests here
- from django.urls import clear_url_caches
- clear_url_caches()
- def tearDown(self):
- from django.urls import clear_url_caches
- clear_url_caches()
- def test_serve(self):
- response = self.client.get('/events/christmas/')
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.templates[0].name, 'tests/event_page.html')
- christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
- self.assertEqual(response.context['self'], christmas_page)
- self.assertContains(response, '<h1>Christmas</h1>')
- self.assertContains(response, '<h2>Event</h2>')
- @override_settings(ROOT_URLCONF='wagtail.tests.non_root_urls')
- def test_serve_with_non_root_urls(self):
- response = self.client.get('/site/events/christmas/')
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.templates[0].name, 'tests/event_page.html')
- christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
- self.assertEqual(response.context['self'], christmas_page)
- self.assertContains(response, '<h1>Christmas</h1>')
- self.assertContains(response, '<h2>Event</h2>')
- def test_serve_unknown_page_returns_404(self):
- response = self.client.get('/events/quinquagesima/')
- self.assertEqual(response.status_code, 404)
- def test_serve_unpublished_page_returns_404(self):
- response = self.client.get('/events/tentative-unpublished-event/')
- self.assertEqual(response.status_code, 404)
- @override_settings(ALLOWED_HOSTS=['localhost', 'events.example.com'])
- def test_serve_with_multiple_sites(self):
- events_page = Page.objects.get(url_path='/home/events/')
- Site.objects.create(hostname='events.example.com', root_page=events_page)
- response = self.client.get('/christmas/', HTTP_HOST='events.example.com')
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.templates[0].name, 'tests/event_page.html')
- christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
- self.assertEqual(response.context['self'], christmas_page)
- self.assertContains(response, '<h1>Christmas</h1>')
- self.assertContains(response, '<h2>Event</h2>')
- # same request to the default host should return a 404
- c = Client()
- response = c.get('/christmas/', HTTP_HOST='localhost')
- self.assertEqual(response.status_code, 404)
- def test_serve_with_custom_context(self):
- response = self.client.get('/events/')
- self.assertEqual(response.status_code, 200)
- # should render the whole page
- self.assertContains(response, '<h1>Events</h1>')
- # response should contain data from the custom 'events' context variable
- self.assertContains(response, '<a href="/events/christmas/">Christmas</a>')
- def test_ajax_response(self):
- response = self.client.get('/events/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
- self.assertEqual(response.status_code, 200)
- # should only render the content of includes/event_listing.html, not the whole page
- self.assertNotContains(response, '<h1>Events</h1>')
- self.assertContains(response, '<a href="/events/christmas/">Christmas</a>')
- def test_before_serve_hook(self):
- response = self.client.get('/events/', HTTP_USER_AGENT='GoogleBot')
- self.assertContains(response, 'bad googlebot no cookie')
- class TestStaticSitePaths(TestCase):
- def setUp(self):
- self.root_page = Page.objects.get(id=1)
- # For simple tests
- self.home_page = self.root_page.add_child(
- instance=SimplePage(title="Homepage", slug="home2", content="hello")
- )
- self.about_page = self.home_page.add_child(
- instance=SimplePage(title="About us", slug="about", content="hello")
- )
- self.contact_page = self.home_page.add_child(
- instance=SimplePage(title="Contact", slug="contact", content="hello")
- )
- # For custom tests
- self.event_index = self.root_page.add_child(instance=EventIndex(title="Events", slug="events"))
- for i in range(20):
- self.event_index.add_child(instance=EventPage(
- title="Event " + str(i), slug="event" + str(i),
- location='the moon', audience='public',
- cost='free', date_from='2001-01-01',
- ))
- def test_local_static_site_paths(self):
- paths = list(self.about_page.get_static_site_paths())
- self.assertEqual(paths, ['/'])
- def test_child_static_site_paths(self):
- paths = list(self.home_page.get_static_site_paths())
- self.assertEqual(paths, ['/', '/about/', '/contact/'])
- def test_custom_static_site_paths(self):
- paths = list(self.event_index.get_static_site_paths())
- # Event index path
- expected_paths = ['/']
- # One path for each page of results
- expected_paths.extend(['/' + str(i + 1) + '/' for i in range(5)])
- # One path for each event page
- expected_paths.extend(['/event' + str(i) + '/' for i in range(20)])
- paths.sort()
- expected_paths.sort()
- self.assertEqual(paths, expected_paths)
- class TestMovePage(TestCase):
- fixtures = ['test.json']
- def test_move_page(self):
- about_us_page = SimplePage.objects.get(url_path='/home/about-us/')
- events_index = EventIndex.objects.get(url_path='/home/events/')
- events_index.move(about_us_page, pos='last-child')
- # re-fetch events index to confirm that db fields have been updated
- events_index = EventIndex.objects.get(id=events_index.id)
- self.assertEqual(events_index.url_path, '/home/about-us/events/')
- self.assertEqual(events_index.depth, 4)
- self.assertEqual(events_index.get_parent().id, about_us_page.id)
- # children of events_index should also have been updated
- christmas = events_index.get_children().get(slug='christmas')
- self.assertEqual(christmas.depth, 5)
- self.assertEqual(christmas.url_path, '/home/about-us/events/christmas/')
- class TestPrevNextSiblings(TestCase):
- fixtures = ['test.json']
- def test_get_next_siblings(self):
- christmas_event = Page.objects.get(url_path='/home/events/christmas/')
- self.assertTrue(christmas_event.get_next_siblings().filter(url_path='/home/events/final-event/').exists())
- def test_get_next_siblings_inclusive(self):
- christmas_event = Page.objects.get(url_path='/home/events/christmas/')
- # First element must always be the current page
- self.assertEqual(christmas_event.get_next_siblings(inclusive=True).first(), christmas_event)
- def test_get_prev_siblings(self):
- final_event = Page.objects.get(url_path='/home/events/final-event/')
- self.assertTrue(final_event.get_prev_siblings().filter(url_path='/home/events/christmas/').exists())
- # First element must always be the current page
- self.assertEqual(final_event.get_prev_siblings(inclusive=True).first(), final_event)
- class TestLiveRevision(TestCase):
- fixtures = ['test.json']
- @freeze_time("2017-01-01 12:00:00")
- def test_publish_method_will_set_live_revision(self):
- page = Page.objects.get(id=2)
- revision = page.save_revision()
- revision.publish()
- page.refresh_from_db()
- self.assertEqual(page.live_revision, revision)
- self.assertEqual(page.last_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
- # first_published_at should not change
- self.assertEqual(page.first_published_at, datetime.datetime(2014, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
- @freeze_time("2017-01-01 12:00:00")
- def test_unpublish_method_will_clean_live_revision(self):
- page = Page.objects.get(id=2)
- revision = page.save_revision()
- revision.publish()
- page.refresh_from_db()
- page.unpublish()
- page.refresh_from_db()
- self.assertIsNone(page.live_revision)
- # first_published_at / last_published_at should remain unchanged on unpublish
- self.assertEqual(page.first_published_at, datetime.datetime(2014, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
- self.assertEqual(page.last_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
- @freeze_time("2017-01-01 12:00:00")
- def test_copy_method_with_keep_live_will_update_live_revision(self):
- about_us = SimplePage.objects.get(url_path='/home/about-us/')
- revision = about_us.save_revision()
- revision.publish()
- new_about_us = about_us.copy(keep_live=True, update_attrs={'title': "New about us", 'slug': 'new-about-us'})
- self.assertIsNotNone(new_about_us.live_revision)
- self.assertNotEqual(about_us.live_revision, new_about_us.live_revision)
- # first_published_at / last_published_at should reflect the current time,
- # not the source page's publish dates, since the copied page is being published
- # for the first time
- self.assertEqual(new_about_us.first_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
- self.assertEqual(new_about_us.last_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
- def test_copy_method_without_keep_live_will_not_update_live_revision(self):
- about_us = SimplePage.objects.get(url_path='/home/about-us/')
- revision = about_us.save_revision()
- revision.publish()
- about_us.refresh_from_db()
- self.assertIsNotNone(about_us.live_revision)
- new_about_us = about_us.copy(keep_live=False, update_attrs={'title': "New about us", 'slug': 'new-about-us'})
- self.assertIsNone(new_about_us.live_revision)
- # first_published_at / last_published_at should be blank, because the copied article
- # has not been published
- self.assertIsNone(new_about_us.first_published_at)
- self.assertIsNone(new_about_us.last_published_at)
- @freeze_time("2017-01-01 12:00:00")
- def test_publish_with_future_go_live_does_not_set_live_revision(self):
- about_us = SimplePage.objects.get(url_path='/home/about-us/')
- about_us.go_live_at = datetime.datetime(2018, 1, 1, 12, 0, 0, tzinfo=pytz.utc)
- revision = about_us.save_revision()
- revision.publish()
- about_us.refresh_from_db()
- self.assertFalse(about_us.live)
- self.assertIsNone(about_us.live_revision)
- # first_published_at / last_published_at should remain unchanged
- self.assertEqual(about_us.first_published_at, datetime.datetime(2014, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
- self.assertEqual(about_us.last_published_at, datetime.datetime(2014, 2, 1, 12, 0, 0, tzinfo=pytz.utc))
- class TestCopyPage(TestCase):
- fixtures = ['test.json']
- def test_copy_page_copies(self):
- about_us = SimplePage.objects.get(url_path='/home/about-us/')
- # Copy it
- new_about_us = about_us.copy(update_attrs={'title': "New about us", 'slug': 'new-about-us'})
- # Check that new_about_us is correct
- self.assertIsInstance(new_about_us, SimplePage)
- self.assertEqual(new_about_us.title, "New about us")
- self.assertEqual(new_about_us.slug, 'new-about-us')
- # Check that new_about_us is a different page
- self.assertNotEqual(about_us.id, new_about_us.id)
- # Check that the url path was updated
- self.assertEqual(new_about_us.url_path, '/home/new-about-us/')
- def test_copy_page_copies_child_objects(self):
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
- )
- # Check that the speakers were copied
- self.assertEqual(new_christmas_event.speakers.count(), 1, "Child objects weren't copied")
- # Check that the speakers weren't removed from old page
- self.assertEqual(christmas_event.speakers.count(), 1, "Child objects were removed from the original page")
- # Check that advert placements were also copied (there's a gotcha here, since the advert_placements
- # relation is defined on Page, not EventPage)
- self.assertEqual(
- new_christmas_event.advert_placements.count(), 1, "Child objects defined on the superclass weren't copied"
- )
- self.assertEqual(
- christmas_event.advert_placements.count(),
- 1,
- "Child objects defined on the superclass were removed from the original page"
- )
- def test_copy_page_copies_revisions(self):
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- christmas_event.save_revision()
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
- )
- # Check that the revisions were copied
- # Copying creates a new revision so we're expecting the new page to have two revisions
- self.assertEqual(new_christmas_event.revisions.count(), 2)
- # Check that the revisions weren't removed from old page
- self.assertEqual(christmas_event.revisions.count(), 1, "Revisions were removed from the original page")
- # Check that the attributes were updated in the latest revision
- latest_revision = new_christmas_event.get_latest_revision_as_page()
- self.assertEqual(latest_revision.title, "New christmas event")
- self.assertEqual(latest_revision.slug, 'new-christmas-event')
- # get_latest_revision_as_page might bypass the revisions table if it determines
- # that there are no draft edits since publish - so retrieve it explicitly from the
- # revision data, to ensure it's been updated there too
- latest_revision = new_christmas_event.get_latest_revision().as_page_object()
- self.assertEqual(latest_revision.title, "New christmas event")
- self.assertEqual(latest_revision.slug, 'new-christmas-event')
- # Check that the ids within the revision were updated correctly
- new_revision = new_christmas_event.revisions.first()
- new_revision_content = json.loads(new_revision.content_json)
- self.assertEqual(new_revision_content['pk'], new_christmas_event.id)
- self.assertEqual(new_revision_content['speakers'][0]['page'], new_christmas_event.id)
- # Also, check that the child objects in the new revision are given new IDs
- old_speakers_ids = set(christmas_event.speakers.values_list('id', flat=True))
- new_speakers_ids = set(speaker['pk'] for speaker in new_revision_content['speakers'])
- self.assertFalse(
- old_speakers_ids.intersection(new_speakers_ids),
- "Child objects in revisions were not given a new primary key"
- )
- def test_copy_page_copies_revisions_and_doesnt_submit_for_moderation(self):
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- christmas_event.save_revision(submitted_for_moderation=True)
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
- )
- # Check that the old revision is still submitted for moderation
- self.assertTrue(christmas_event.revisions.order_by('created_at').first().submitted_for_moderation)
- # Check that the new revision is not submitted for moderation
- self.assertFalse(new_christmas_event.revisions.order_by('created_at').first().submitted_for_moderation)
- def test_copy_page_copies_revisions_and_doesnt_change_created_at(self):
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- christmas_event.save_revision(submitted_for_moderation=True)
- # Set the created_at of the revision to a time in the past
- revision = christmas_event.get_latest_revision()
- revision.created_at = datetime.datetime(2014, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
- revision.save()
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
- )
- # Check that the created_at time is the same
- christmas_event_created_at = christmas_event.revisions.order_by('created_at').first().created_at
- new_christmas_event_created_at = new_christmas_event.revisions.order_by('created_at').first().created_at
- self.assertEqual(christmas_event_created_at, new_christmas_event_created_at)
- def test_copy_page_copies_revisions_and_doesnt_schedule(self):
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- christmas_event.save_revision(approved_go_live_at=datetime.datetime(2014, 9, 16, 9, 12, 00, tzinfo=pytz.utc))
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
- )
- # Check that the old revision is still scheduled
- self.assertEqual(
- christmas_event.revisions.order_by('created_at').first().approved_go_live_at,
- datetime.datetime(2014, 9, 16, 9, 12, 00, tzinfo=pytz.utc)
- )
- # Check that the new revision is not scheduled
- self.assertEqual(new_christmas_event.revisions.order_by('created_at').first().approved_go_live_at, None)
- def test_copy_page_doesnt_copy_revisions_if_told_not_to_do_so(self):
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- christmas_event.save_revision()
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}, copy_revisions=False
- )
- # Check that the revisions weren't copied
- # Copying creates a new revision so we're expecting the new page to have one revision
- self.assertEqual(new_christmas_event.revisions.count(), 1)
- # Check that the revisions weren't removed from old page
- self.assertEqual(christmas_event.revisions.count(), 1, "Revisions were removed from the original page")
- def test_copy_page_copies_child_objects_with_nonspecific_class(self):
- # Get chrismas page as Page instead of EventPage
- christmas_event = Page.objects.get(url_path='/home/events/christmas/')
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
- )
- # Check that the type of the new page is correct
- self.assertIsInstance(new_christmas_event, EventPage)
- # Check that the speakers were copied
- self.assertEqual(new_christmas_event.speakers.count(), 1, "Child objects weren't copied")
- def test_copy_page_copies_recursively(self):
- events_index = EventIndex.objects.get(url_path='/home/events/')
- # Copy it
- new_events_index = events_index.copy(
- recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}
- )
- # Get christmas event
- old_christmas_event = events_index.get_children().filter(slug='christmas').first()
- new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
- # Check that the event exists in both places
- self.assertNotEqual(new_christmas_event, None, "Child pages weren't copied")
- self.assertNotEqual(old_christmas_event, None, "Child pages were removed from original page")
- # Check that the url path was updated
- self.assertEqual(new_christmas_event.url_path, '/home/new-events-index/christmas/')
- def test_copy_page_copies_recursively_with_child_objects(self):
- events_index = EventIndex.objects.get(url_path='/home/events/')
- # Copy it
- new_events_index = events_index.copy(
- recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}
- )
- # Get christmas event
- old_christmas_event = events_index.get_children().filter(slug='christmas').first()
- new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
- # Check that the speakers were copied
- self.assertEqual(new_christmas_event.specific.speakers.count(), 1, "Child objects weren't copied")
- # Check that the speakers weren't removed from old page
- self.assertEqual(
- old_christmas_event.specific.speakers.count(), 1, "Child objects were removed from the original page"
- )
- def test_copy_page_copies_recursively_with_revisions(self):
- events_index = EventIndex.objects.get(url_path='/home/events/')
- old_christmas_event = events_index.get_children().filter(slug='christmas').first().specific
- old_christmas_event.save_revision()
- # Copy it
- new_events_index = events_index.copy(
- recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}
- )
- # Get christmas event
- new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
- # Check that the revisions were copied
- # Copying creates a new revision so we're expecting the new page to have two revisions
- self.assertEqual(new_christmas_event.specific.revisions.count(), 2)
- # Check that the revisions weren't removed from old page
- self.assertEqual(
- old_christmas_event.specific.revisions.count(), 1, "Revisions were removed from the original page"
- )
- def test_copy_page_copies_recursively_but_doesnt_copy_revisions_if_told_not_to_do_so(self):
- events_index = EventIndex.objects.get(url_path='/home/events/')
- old_christmas_event = events_index.get_children().filter(slug='christmas').first()
- old_christmas_event.save_revision()
- # Copy it
- new_events_index = events_index.copy(
- recursive=True,
- update_attrs={'title': "New events index", 'slug': 'new-events-index'},
- copy_revisions=False
- )
- # Get christmas event
- new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
- # Check that the revisions weren't copied
- # Copying creates a new revision so we're expecting the new page to have one revision
- self.assertEqual(new_christmas_event.specific.revisions.count(), 1)
- # Check that the revisions weren't removed from old page
- self.assertEqual(
- old_christmas_event.specific.revisions.count(), 1, "Revisions were removed from the original page"
- )
- def test_copy_page_copies_recursively_to_the_same_tree(self):
- events_index = EventIndex.objects.get(url_path='/home/events/')
- old_christmas_event = events_index.get_children().filter(slug='christmas').first().specific
- old_christmas_event.save_revision()
- with self.assertRaises(Exception) as exception:
- events_index.copy(
- recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}, to=events_index
- )
- self.assertEqual(str(exception.exception), "You cannot copy a tree branch recursively into itself")
- def test_copy_page_updates_user(self):
- event_moderator = get_user_model().objects.get(username='eventmoderator')
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- christmas_event.save_revision()
- # Copy it
- new_christmas_event = christmas_event.copy(
- update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'},
- user=event_moderator,
- )
- # Check that the owner has been updated
- self.assertEqual(new_christmas_event.owner, event_moderator)
- # Check that the user on the last revision is correct
- self.assertEqual(new_christmas_event.get_latest_revision().user, event_moderator)
- def test_copy_multi_table_inheritance(self):
- saint_patrick_event = SingleEventPage.objects.get(url_path='/home/events/saint-patrick/')
- # Copy it
- new_saint_patrick_event = saint_patrick_event.copy(update_attrs={'slug': 'new-saint-patrick'})
- # Check that new_saint_patrick_event is correct
- self.assertIsInstance(new_saint_patrick_event, SingleEventPage)
- self.assertEqual(new_saint_patrick_event.excerpt, saint_patrick_event.excerpt)
- # Check that new_saint_patrick_event is a different page, including parents from both EventPage and Page
- self.assertNotEqual(saint_patrick_event.id, new_saint_patrick_event.id)
- self.assertNotEqual(saint_patrick_event.eventpage_ptr.id, new_saint_patrick_event.eventpage_ptr.id)
- self.assertNotEqual(
- saint_patrick_event.eventpage_ptr.page_ptr.id,
- new_saint_patrick_event.eventpage_ptr.page_ptr.id
- )
- # Check that the url path was updated
- self.assertEqual(new_saint_patrick_event.url_path, '/home/events/new-saint-patrick/')
- # Check that both parent instance exists
- self.assertIsInstance(EventPage.objects.get(id=new_saint_patrick_event.id), EventPage)
- self.assertIsInstance(Page.objects.get(id=new_saint_patrick_event.id), Page)
- def test_copy_page_copies_tags(self):
- # create and publish a TaggedPage under Events
- event_index = Page.objects.get(url_path='/home/events/')
- tagged_page = TaggedPage(title='My tagged page', slug='my-tagged-page')
- tagged_page.tags.add('wagtail', 'bird')
- event_index.add_child(instance=tagged_page)
- tagged_page.save_revision().publish()
- old_tagged_item_ids = [item.id for item in tagged_page.tagged_items.all()]
- # there should be two items here, with defined (truthy) IDs
- self.assertEqual(len(old_tagged_item_ids), 2)
- self.assertTrue(all(old_tagged_item_ids))
- # copy to underneath homepage
- homepage = Page.objects.get(url_path='/home/')
- new_tagged_page = tagged_page.copy(to=homepage)
- self.assertNotEqual(tagged_page.id, new_tagged_page.id)
- # new page should also have two tags
- new_tagged_item_ids = [item.id for item in new_tagged_page.tagged_items.all()]
- self.assertEqual(len(new_tagged_item_ids), 2)
- self.assertTrue(all(new_tagged_item_ids))
- # new tagged_item IDs should differ from old ones
- self.assertTrue(all([
- item_id not in old_tagged_item_ids
- for item_id in new_tagged_item_ids
- ]))
- def test_copy_page_with_m2m_relations(self):
- # create and publish a ManyToManyBlogPage under Events
- event_index = Page.objects.get(url_path='/home/events/')
- category = BlogCategory.objects.create(name='Birds')
- advert = Advert.objects.create(url='http://www.heinz.com/', text="beanz meanz heinz")
- blog_page = ManyToManyBlogPage(title='My blog page', slug='my-blog-page')
- event_index.add_child(instance=blog_page)
- blog_page.adverts.add(advert)
- BlogCategoryBlogPage.objects.create(category=category, page=blog_page)
- blog_page.save_revision().publish()
- # copy to underneath homepage
- homepage = Page.objects.get(url_path='/home/')
- new_blog_page = blog_page.copy(to=homepage)
- # M2M relations are not formally supported, so for now we're only interested in
- # the copy operation as a whole succeeding, rather than the child objects being copied
- self.assertNotEqual(blog_page.id, new_blog_page.id)
- def test_copy_page_with_generic_foreign_key(self):
- # create and publish a GenericSnippetPage under Events
- event_index = Page.objects.get(url_path='/home/events/')
- advert = Advert.objects.create(url='http://www.heinz.com/', text="beanz meanz heinz")
- page = GenericSnippetPage(title='My snippet page', slug='my-snippet-page')
- page.snippet_content_object = advert
- event_index.add_child(instance=page)
- page.save_revision().publish()
- # copy to underneath homepage
- homepage = Page.objects.get(url_path='/home/')
- new_page = page.copy(to=homepage)
- self.assertNotEqual(page.id, new_page.id)
- self.assertEqual(new_page.snippet_content_object, advert)
- def test_copy_page_with_o2o_relation(self):
- event_index = Page.objects.get(url_path='/home/events/')
- page = OneToOnePage(title='My page', slug='my-page')
- event_index.add_child(instance=page)
- homepage = Page.objects.get(url_path='/home/')
- new_page = page.copy(to=homepage)
- self.assertNotEqual(page.id, new_page.id)
- def test_copy_page_with_additional_excluded_fields(self):
- homepage = Page.objects.get(url_path='/home/')
- page = PageWithExcludedCopyField(
- title='Discovery',
- slug='disco',
- content='NCC-1031',
- special_field='Context is for Kings')
- new_page = page.copy(to=homepage)
- self.assertEqual(page.title, new_page.title)
- self.assertNotEqual(page.id, new_page.id)
- self.assertNotEqual(page.path, new_page.path)
- # special_field is in the list to be excluded
- self.assertNotEqual(page.special_field, new_page.special_field)
- class TestSubpageTypeBusinessRules(TestCase, WagtailTestUtils):
- def test_allowed_subpage_models(self):
- # SimplePage does not define any restrictions on subpage types
- # SimplePage is a valid subpage of SimplePage
- self.assertIn(SimplePage, SimplePage.allowed_subpage_models())
- # BusinessIndex is a valid subpage of SimplePage
- self.assertIn(BusinessIndex, SimplePage.allowed_subpage_models())
- # BusinessSubIndex is not valid, because it explicitly omits SimplePage from parent_page_types
- self.assertNotIn(BusinessSubIndex, SimplePage.allowed_subpage_models())
- # BusinessChild has an empty subpage_types list, so does not allow anything
- self.assertNotIn(SimplePage, BusinessChild.allowed_subpage_models())
- self.assertNotIn(BusinessIndex, BusinessChild.allowed_subpage_models())
- self.assertNotIn(BusinessSubIndex, BusinessChild.allowed_subpage_models())
- # BusinessSubIndex only allows BusinessChild as subpage type
- self.assertNotIn(SimplePage, BusinessSubIndex.allowed_subpage_models())
- self.assertIn(BusinessChild, BusinessSubIndex.allowed_subpage_models())
- def test_allowed_parent_page_models(self):
- # SimplePage does not define any restrictions on parent page types
- # SimplePage is a valid parent page of SimplePage
- self.assertIn(SimplePage, SimplePage.allowed_parent_page_models())
- # BusinessChild cannot be a parent of anything
- self.assertNotIn(BusinessChild, SimplePage.allowed_parent_page_models())
- # BusinessNowherePage does not allow anything as a parent
- self.assertNotIn(SimplePage, BusinessNowherePage.allowed_parent_page_models())
- self.assertNotIn(StandardIndex, BusinessNowherePage.allowed_parent_page_models())
- # BusinessSubIndex only allows BusinessIndex as a parent
- self.assertNotIn(SimplePage, BusinessSubIndex.allowed_parent_page_models())
- self.assertIn(BusinessIndex, BusinessSubIndex.allowed_parent_page_models())
- def test_can_exist_under(self):
- self.assertTrue(SimplePage.can_exist_under(SimplePage()))
- # StandardIndex should only be allowed under a Page
- self.assertTrue(StandardIndex.can_exist_under(Page()))
- self.assertFalse(StandardIndex.can_exist_under(SimplePage()))
- # The Business pages are quite restrictive in their structure
- self.assertTrue(BusinessSubIndex.can_exist_under(BusinessIndex()))
- self.assertTrue(BusinessChild.can_exist_under(BusinessIndex()))
- self.assertTrue(BusinessChild.can_exist_under(BusinessSubIndex()))
- self.assertFalse(BusinessSubIndex.can_exist_under(SimplePage()))
- self.assertFalse(BusinessSubIndex.can_exist_under(BusinessSubIndex()))
- self.assertFalse(BusinessChild.can_exist_under(SimplePage()))
- def test_can_create_at(self):
- # Pages are not `is_creatable`, and should not be creatable
- self.assertFalse(Page.can_create_at(Page()))
- # SimplePage can be created under a simple page
- self.assertTrue(SimplePage.can_create_at(SimplePage()))
- # StandardIndex can be created under a Page, but not a SimplePage
- self.assertTrue(StandardIndex.can_create_at(Page()))
- self.assertFalse(StandardIndex.can_create_at(SimplePage()))
- # The Business pages are quite restrictive in their structure
- self.assertTrue(BusinessSubIndex.can_create_at(BusinessIndex()))
- self.assertTrue(BusinessChild.can_create_at(BusinessIndex()))
- self.assertTrue(BusinessChild.can_create_at(BusinessSubIndex()))
- self.assertFalse(BusinessChild.can_create_at(SimplePage()))
- self.assertFalse(BusinessSubIndex.can_create_at(SimplePage()))
- def test_can_move_to(self):
- self.assertTrue(SimplePage().can_move_to(SimplePage()))
- # StandardIndex should only be allowed under a Page
- self.assertTrue(StandardIndex().can_move_to(Page()))
- self.assertFalse(StandardIndex().can_move_to(SimplePage()))
- # The Business pages are quite restrictive in their structure
- self.assertTrue(BusinessSubIndex().can_move_to(BusinessIndex()))
- self.assertTrue(BusinessChild().can_move_to(BusinessIndex()))
- self.assertTrue(BusinessChild().can_move_to(BusinessSubIndex()))
- self.assertFalse(BusinessChild().can_move_to(SimplePage()))
- self.assertFalse(BusinessSubIndex().can_move_to(SimplePage()))
- def test_singleton_page_creation(self):
- root_page = Page.objects.get(url_path='/home/')
- # A single singleton page should be creatable
- self.assertTrue(SingletonPage.can_create_at(root_page))
- # Create a singleton page
- root_page.add_child(instance=SingletonPage(
- title='singleton', slug='singleton'))
- # A second singleton page should not be creatable
- self.assertFalse(SingletonPage.can_create_at(root_page))
- class TestIssue735(TestCase):
- """
- Issue 735 reports that URL paths of child pages are not
- updated correctly when slugs of parent pages are updated
- """
- fixtures = ['test.json']
- def test_child_urls_updated_on_parent_publish(self):
- event_index = Page.objects.get(url_path='/home/events/')
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- # Change the event index slug and publish it
- event_index.slug = 'old-events'
- event_index.save_revision().publish()
- # Check that the christmas events url path updated correctly
- new_christmas_event = EventPage.objects.get(id=christmas_event.id)
- self.assertEqual(new_christmas_event.url_path, '/home/old-events/christmas/')
- class TestIssue756(TestCase):
- """
- Issue 756 reports that the latest_revision_created_at
- field was getting clobbered whenever a revision was published
- """
- def test_publish_revision_doesnt_remove_latest_revision_created_at(self):
- # Create a revision
- revision = Page.objects.get(id=1).save_revision()
- # Check that latest_revision_created_at is set
- self.assertIsNotNone(Page.objects.get(id=1).latest_revision_created_at)
- # Publish the revision
- revision.publish()
- # Check that latest_revision_created_at is still set
- self.assertIsNotNone(Page.objects.get(id=1).latest_revision_created_at)
- class TestIssue1216(TestCase):
- """
- Test that url paths greater than 255 characters are supported
- """
- fixtures = ['test.json']
- def test_url_path_can_exceed_255_characters(self):
- event_index = Page.objects.get(url_path='/home/events/')
- christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
- # Change the christmas_event slug first - this way, we test that the process for
- # updating child url paths also handles >255 character paths correctly
- new_christmas_slug = "christmas-%s-christmas" % ("0123456789" * 20)
- christmas_event.slug = new_christmas_slug
- christmas_event.save_revision().publish()
- # Change the event index slug and publish it
- new_event_index_slug = "events-%s-events" % ("0123456789" * 20)
- event_index.slug = new_event_index_slug
- event_index.save_revision().publish()
- # Check that the url path updated correctly
- new_christmas_event = EventPage.objects.get(id=christmas_event.id)
- expected_url_path = "/home/%s/%s/" % (new_event_index_slug, new_christmas_slug)
- self.assertEqual(new_christmas_event.url_path, expected_url_path)
- class TestIsCreatable(TestCase):
- def test_is_creatable_default(self):
- """By default, pages should be creatable"""
- self.assertTrue(SimplePage.is_creatable)
- self.assertIn(SimplePage, get_page_models())
- def test_is_creatable_false(self):
- """Page types should be able to disable their creation"""
- self.assertFalse(MTIBasePage.is_creatable)
- # non-creatable pages should still appear in the get_page_models list
- self.assertIn(MTIBasePage, get_page_models())
- def test_is_creatable_not_inherited(self):
- """
- is_creatable should not be inherited in the normal manner, and should
- default to True unless set otherwise
- """
- self.assertTrue(MTIChildPage.is_creatable)
- self.assertIn(MTIChildPage, get_page_models())
- def test_abstract_pages(self):
- """
- Abstract models should not be creatable
- """
- self.assertFalse(AbstractPage.is_creatable)
- self.assertNotIn(AbstractPage, get_page_models())
- class TestDeferredPageClasses(TestCase):
- def test_deferred_page_classes_are_not_registered(self):
- """
- In Django <1.10, a call to `defer` such as `SimplePage.objects.defer('content')`
- will dynamically create a subclass of SimplePage. Ensure that these subclasses
- are not registered in the get_page_models() list
- """
- list(SimplePage.objects.defer('content'))
- simplepage_subclasses = [cls for cls in get_page_models() if issubclass(cls, SimplePage)]
- self.assertEqual(simplepage_subclasses, [SimplePage])
- class TestPageManager(TestCase):
- def test_page_manager(self):
- """
- Assert that the Page class uses PageManager
- """
- self.assertIs(type(Page.objects), PageManager)
- def test_page_subclass_manager(self):
- """
- Assert that Page subclasses get a PageManager without having to do
- anything special. MTI subclasses do *not* inherit their parents Manager
- by default.
- """
- self.assertIs(type(SimplePage.objects), PageManager)
- def test_custom_page_manager(self):
- """
- Subclasses should be able to override their default Manager, and
- Wagtail should respect this. It is up to the developer to ensure their
- custom Manager inherits from PageManager.
- """
- self.assertIs(type(CustomManagerPage.objects), CustomManager)
- def test_custom_page_queryset(self):
- """
- Managers that are constructed from a custom PageQuerySet
- (via PageManager.from_queryset(CustomPageQuerySet)) should return
- querysets of that type
- """
- self.assertIs(type(CustomManagerPage.objects.all()), CustomPageQuerySet)
- self.assertIs(type(CustomManagerPage.objects.about_spam()), CustomPageQuerySet)
- self.assertIs(type(CustomManagerPage.objects.all().about_spam()), CustomPageQuerySet)
- self.assertIs(type(CustomManagerPage.objects.about_spam().all()), CustomPageQuerySet)
- def test_abstract_base_page_manager(self):
- """
- Abstract base classes should be able to override their default Manager,
- and Wagtail should respect this. It is up to the developer to ensure
- their custom Manager inherits from PageManager.
- """
- self.assertIs(type(MyCustomPage.objects), CustomManager)
- class TestIssue2024(TestCase):
- """
- This tests that deleting a content type can't delete any Page objects.
- """
- fixtures = ['test.json']
- def test_delete_content_type(self):
- event_index = Page.objects.get(url_path='/home/events/')
- # Delete the content type
- event_index_content_type = event_index.content_type
- event_index_content_type.delete()
- # Fetch the page again, it should still exist
- event_index = Page.objects.get(url_path='/home/events/')
- # Check that the content_type changed to Page
- self.assertEqual(event_index.content_type, ContentType.objects.get_for_model(Page))
- @override_settings(ALLOWED_HOSTS=['localhost'])
- class TestDummyRequest(TestCase):
- fixtures = ['test.json']
- def test_dummy_request_for_accessible_page(self):
- event_index = Page.objects.get(url_path='/home/events/')
- request = event_index.dummy_request()
- # request should have the correct path and hostname for this page
- self.assertEqual(request.path, '/events/')
- self.assertEqual(request.META['HTTP_HOST'], 'localhost')
- # check other env vars required by the WSGI spec
- self.assertEqual(request.META['REQUEST_METHOD'], 'GET')
- self.assertEqual(request.META['SCRIPT_NAME'], '')
- self.assertEqual(request.META['PATH_INFO'], '/events/')
- self.assertEqual(request.META['SERVER_NAME'], 'localhost')
- self.assertEqual(request.META['SERVER_PORT'], 80)
- self.assertEqual(request.META['SERVER_PROTOCOL'], 'HTTP/1.1')
- self.assertEqual(request.META['wsgi.version'], (1, 0))
- self.assertEqual(request.META['wsgi.url_scheme'], 'http')
- self.assertIn('wsgi.input', request.META)
- self.assertIn('wsgi.errors', request.META)
- self.assertIn('wsgi.multithread', request.META)
- self.assertIn('wsgi.multiprocess', request.META)
- self.assertIn('wsgi.run_once', request.META)
- def test_dummy_request_for_accessible_page_with_original_request(self):
- event_index = Page.objects.get(url_path='/home/events/')
- original_headers = {
- 'REMOTE_ADDR': '192.168.0.1',
- 'HTTP_X_FORWARDED_FOR': '192.168.0.2,192.168.0.3',
- 'HTTP_COOKIE': "test=1;blah=2",
- 'HTTP_USER_AGENT': "Test Agent",
- }
- factory = RequestFactory(**original_headers)
- original_request = factory.get('/home/events/')
- request = event_index.dummy_request(original_request)
- # request should have the all the special headers we set in original_request
- self.assertEqual(request.META['REMOTE_ADDR'], original_request.META['REMOTE_ADDR'])
- self.assertEqual(request.META['HTTP_X_FORWARDED_FOR'], original_request.META['HTTP_X_FORWARDED_FOR'])
- self.assertEqual(request.META['HTTP_COOKIE'], original_request.META['HTTP_COOKIE'])
- self.assertEqual(request.META['HTTP_USER_AGENT'], original_request.META['HTTP_USER_AGENT'])
- # check other env vars required by the WSGI spec
- self.assertEqual(request.META['REQUEST_METHOD'], 'GET')
- self.assertEqual(request.META['SCRIPT_NAME'], '')
- self.assertEqual(request.META['PATH_INFO'], '/events/')
- self.assertEqual(request.META['SERVER_NAME'], 'localhost')
- self.assertEqual(request.META['SERVER_PORT'], 80)
- self.assertEqual(request.META['SERVER_PROTOCOL'], 'HTTP/1.1')
- self.assertEqual(request.META['wsgi.version'], (1, 0))
- self.assertEqual(request.META['wsgi.url_scheme'], 'http')
- self.assertIn('wsgi.input', request.META)
- self.assertIn('wsgi.errors', request.META)
- self.assertIn('wsgi.multithread', request.META)
- self.assertIn('wsgi.multiprocess', request.META)
- self.assertIn('wsgi.run_once', request.META)
- @override_settings(ALLOWED_HOSTS=['production.example.com'])
- def test_dummy_request_for_inaccessible_page_should_use_valid_host(self):
- root_page = Page.objects.get(url_path='/')
- request = root_page.dummy_request()
- # in the absence of an actual Site record where we can access this page,
- # dummy_request should still provide a hostname that Django's host header
- # validation won't reject
- self.assertEqual(request.META['HTTP_HOST'], 'production.example.com')
- @override_settings(ALLOWED_HOSTS=['*'])
- def test_dummy_request_for_inaccessible_page_with_wildcard_allowed_hosts(self):
- root_page = Page.objects.get(url_path='/')
- request = root_page.dummy_request()
- # '*' is not a valid hostname, so ensure that we replace it with something sensible
- self.assertNotEqual(request.META['HTTP_HOST'], '*')
- class TestShowInMenusDefaultOption(TestCase):
- """
- This tests that a page model can define the default for 'show_in_menus'
- """
- fixtures = ['test.json']
- def test_show_in_menus_default(self):
- # Create a page that does not have the default init
- page = Page(
- title='My Awesome Page', slug='my-awesome-page')
- # Check that the page instance creates with show_in_menu as False
- self.assertFalse(page.show_in_menus)
- def test_show_in_menus_default_override(self):
- # Create a page that does have the default init
- page = AlwaysShowInMenusPage(
- title='My Awesome Page', slug='my-awesome-page')
- # Check that the page instance creates with show_in_menu as True
- self.assertTrue(page.show_in_menus)
|