test_page_model.py 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422
  1. import datetime
  2. import json
  3. import pytz
  4. from django.contrib.auth import get_user_model
  5. from django.contrib.auth.models import AnonymousUser
  6. from django.contrib.contenttypes.models import ContentType
  7. from django.core.exceptions import ValidationError
  8. from django.http import Http404, HttpRequest
  9. from django.test import Client, TestCase
  10. from django.test.client import RequestFactory
  11. from django.test.utils import override_settings
  12. from freezegun import freeze_time
  13. from wagtail.core.models import Page, PageManager, Site, get_page_models
  14. from wagtail.tests.testapp.models import (
  15. AbstractPage, Advert, AlwaysShowInMenusPage, BlogCategory, BlogCategoryBlogPage, BusinessChild,
  16. BusinessIndex, BusinessNowherePage, BusinessSubIndex, CustomManager, CustomManagerPage,
  17. CustomPageQuerySet, EventIndex, EventPage, GenericSnippetPage, ManyToManyBlogPage, MTIBasePage,
  18. MTIChildPage, MyCustomPage, OneToOnePage, PageWithExcludedCopyField, SimplePage,
  19. SingleEventPage, SingletonPage, StandardIndex, TaggedPage)
  20. from wagtail.tests.utils import WagtailTestUtils
  21. def get_ct(model):
  22. return ContentType.objects.get_for_model(model)
  23. class TestValidation(TestCase):
  24. fixtures = ['test.json']
  25. def test_can_create(self):
  26. """
  27. Check that basic page creation works
  28. """
  29. homepage = Page.objects.get(url_path='/home/')
  30. hello_page = SimplePage(title="Hello world", slug='hello-world', content="hello")
  31. homepage.add_child(instance=hello_page)
  32. # check that hello_page exists in the db
  33. retrieved_page = Page.objects.get(id=hello_page.id)
  34. self.assertEqual(retrieved_page.title, "Hello world")
  35. def test_title_is_required(self):
  36. homepage = Page.objects.get(url_path='/home/')
  37. hello_page = SimplePage(slug='hello-world', content="hello")
  38. with self.assertRaises(ValidationError):
  39. homepage.add_child(instance=hello_page)
  40. hello_page = SimplePage(title="", slug='hello-world', content="hello")
  41. with self.assertRaises(ValidationError):
  42. homepage.add_child(instance=hello_page)
  43. def test_slug_is_autogenerated(self):
  44. homepage = Page.objects.get(url_path='/home/')
  45. # slug should be auto-assigned to a slugified version of the title
  46. hello_page = SimplePage(title="Hello world", content="hello")
  47. homepage.add_child(instance=hello_page)
  48. retrieved_page = Page.objects.get(id=hello_page.id)
  49. self.assertEqual(retrieved_page.slug, 'hello-world')
  50. # auto-generated slug should receive a suffix to make it unique
  51. events_page = SimplePage(title="Events", content="hello")
  52. homepage.add_child(instance=events_page)
  53. retrieved_page = Page.objects.get(id=events_page.id)
  54. self.assertEqual(retrieved_page.slug, 'events-2')
  55. def test_slug_must_be_unique_within_parent(self):
  56. homepage = Page.objects.get(url_path='/home/')
  57. events_page = SimplePage(title="Events", slug='events', content="hello")
  58. with self.assertRaises(ValidationError):
  59. homepage.add_child(instance=events_page)
  60. def test_slug_can_duplicate_other_sections(self):
  61. homepage = Page.objects.get(url_path='/home/')
  62. # the Events section has a page with slug='christmas', but we still allow
  63. # it as a slug elsewhere
  64. christmas_page = SimplePage(title="Christmas", slug='christmas', content="hello")
  65. homepage.add_child(instance=christmas_page)
  66. self.assertTrue(Page.objects.filter(id=christmas_page.id).exists())
  67. def test_get_admin_display_title(self):
  68. homepage = Page.objects.get(url_path='/home/')
  69. self.assertEqual(homepage.draft_title, homepage.get_admin_display_title())
  70. def test_get_admin_display_title_with_blank_draft_title(self):
  71. # Display title should fall back on the live title if draft_title is blank;
  72. # this can happen if the page was created through fixtures or migrations that
  73. # didn't explicitly account for draft_title
  74. # (since draft_title doesn't get populated automatically on save in those cases)
  75. Page.objects.filter(url_path='/home/').update(title='live title', draft_title='')
  76. homepage = Page.objects.get(url_path='/home/')
  77. self.assertEqual(homepage.get_admin_display_title(), 'live title')
  78. def test_draft_title_is_autopopulated(self):
  79. homepage = Page.objects.get(url_path='/home/')
  80. hello_page = SimplePage(title="Hello world", content="hello")
  81. homepage.add_child(instance=hello_page)
  82. retrieved_page = Page.objects.get(id=hello_page.id)
  83. self.assertEqual(retrieved_page.draft_title, "Hello world")
  84. hello_page = SimplePage(title="Hello world", draft_title="Hello world edited", content="hello")
  85. homepage.add_child(instance=hello_page)
  86. retrieved_page = Page.objects.get(id=hello_page.id)
  87. self.assertEqual(retrieved_page.draft_title, "Hello world edited")
  88. @override_settings(ALLOWED_HOSTS=['localhost', 'events.example.com', 'about.example.com', 'unknown.site.com'])
  89. class TestSiteRouting(TestCase):
  90. fixtures = ['test.json']
  91. def setUp(self):
  92. self.default_site = Site.objects.get(is_default_site=True)
  93. events_page = Page.objects.get(url_path='/home/events/')
  94. about_page = Page.objects.get(url_path='/home/about-us/')
  95. self.events_site = Site.objects.create(hostname='events.example.com', root_page=events_page)
  96. self.alternate_port_events_site = Site.objects.create(
  97. hostname='events.example.com',
  98. root_page=events_page,
  99. port='8765'
  100. )
  101. self.about_site = Site.objects.create(hostname='about.example.com', root_page=about_page)
  102. self.alternate_port_default_site = Site.objects.create(hostname=self.default_site.hostname, port='8765', root_page=self.default_site.root_page)
  103. self.unrecognised_port = '8000'
  104. self.unrecognised_hostname = 'unknown.site.com'
  105. def test_no_host_header_routes_to_default_site(self):
  106. # requests without a Host: header should be directed to the default site
  107. request = HttpRequest()
  108. request.path = '/'
  109. with self.assertNumQueries(1):
  110. self.assertEqual(Site.find_for_request(request), self.default_site)
  111. def test_valid_headers_route_to_specific_site(self):
  112. # requests with a known Host: header should be directed to the specific site
  113. request = HttpRequest()
  114. request.path = '/'
  115. request.META['HTTP_HOST'] = self.events_site.hostname
  116. request.META['SERVER_PORT'] = self.events_site.port
  117. with self.assertNumQueries(1):
  118. self.assertEqual(Site.find_for_request(request), self.events_site)
  119. def test_ports_in_request_headers_are_respected(self):
  120. # ports in the Host: header should be respected
  121. request = HttpRequest()
  122. request.path = '/'
  123. request.META['HTTP_HOST'] = self.alternate_port_events_site.hostname
  124. request.META['SERVER_PORT'] = self.alternate_port_events_site.port
  125. with self.assertNumQueries(1):
  126. self.assertEqual(Site.find_for_request(request), self.alternate_port_events_site)
  127. def test_unrecognised_host_header_routes_to_default_site(self):
  128. # requests with an unrecognised Host: header should be directed to the default site
  129. request = HttpRequest()
  130. request.path = '/'
  131. request.META['HTTP_HOST'] = self.unrecognised_hostname
  132. request.META['SERVER_PORT'] = '80'
  133. with self.assertNumQueries(1):
  134. self.assertEqual(Site.find_for_request(request), self.default_site)
  135. def test_unrecognised_port_and_default_host_routes_to_default_site(self):
  136. # requests to the default host on an unrecognised port should be directed to the default site
  137. request = HttpRequest()
  138. request.path = '/'
  139. request.META['HTTP_HOST'] = self.default_site.hostname
  140. request.META['SERVER_PORT'] = self.unrecognised_port
  141. with self.assertNumQueries(1):
  142. self.assertEqual(Site.find_for_request(request), self.default_site)
  143. def test_unrecognised_port_and_unrecognised_host_routes_to_default_site(self):
  144. # requests with an unrecognised Host: header _and_ an unrecognised port
  145. # hould be directed to the default site
  146. request = HttpRequest()
  147. request.path = '/'
  148. request.META['HTTP_HOST'] = self.unrecognised_hostname
  149. request.META['SERVER_PORT'] = self.unrecognised_port
  150. with self.assertNumQueries(1):
  151. self.assertEqual(Site.find_for_request(request), self.default_site)
  152. def test_unrecognised_port_on_known_hostname_routes_there_if_no_ambiguity(self):
  153. # requests on an unrecognised port should be directed to the site with
  154. # matching hostname if there is no ambiguity
  155. request = HttpRequest()
  156. request.path = '/'
  157. request.META['HTTP_HOST'] = self.about_site.hostname
  158. request.META['SERVER_PORT'] = self.unrecognised_port
  159. with self.assertNumQueries(1):
  160. self.assertEqual(Site.find_for_request(request), self.about_site)
  161. def test_unrecognised_port_on_known_hostname_routes_to_default_site_if_ambiguity(self):
  162. # requests on an unrecognised port should be directed to the default
  163. # site, even if their hostname (but not port) matches more than one
  164. # other entry
  165. request = HttpRequest()
  166. request.path = '/'
  167. request.META['HTTP_HOST'] = self.events_site.hostname
  168. request.META['SERVER_PORT'] = self.unrecognised_port
  169. with self.assertNumQueries(1):
  170. self.assertEqual(Site.find_for_request(request), self.default_site)
  171. def test_port_in_http_host_header_is_ignored(self):
  172. # port in the HTTP_HOST header is ignored
  173. request = HttpRequest()
  174. request.path = '/'
  175. request.META['HTTP_HOST'] = "%s:%s" % (self.events_site.hostname, self.events_site.port)
  176. request.META['SERVER_PORT'] = self.alternate_port_events_site.port
  177. with self.assertNumQueries(1):
  178. self.assertEqual(Site.find_for_request(request), self.alternate_port_events_site)
  179. class TestRouting(TestCase):
  180. fixtures = ['test.json']
  181. # need to clear urlresolver caches before/after tests, because we override ROOT_URLCONF
  182. # in some tests here
  183. def setUp(self):
  184. from django.urls import clear_url_caches
  185. clear_url_caches()
  186. def tearDown(self):
  187. from django.urls import clear_url_caches
  188. clear_url_caches()
  189. def test_urls(self):
  190. default_site = Site.objects.get(is_default_site=True)
  191. homepage = Page.objects.get(url_path='/home/')
  192. christmas_page = Page.objects.get(url_path='/home/events/christmas/')
  193. # Basic installation only has one site configured, so page.url will return local URLs
  194. self.assertEqual(
  195. homepage.get_url_parts(),
  196. (default_site.id, 'http://localhost', '/')
  197. )
  198. self.assertEqual(homepage.full_url, 'http://localhost/')
  199. self.assertEqual(homepage.url, '/')
  200. self.assertEqual(homepage.relative_url(default_site), '/')
  201. self.assertEqual(homepage.get_site(), default_site)
  202. self.assertEqual(
  203. christmas_page.get_url_parts(),
  204. (default_site.id, 'http://localhost', '/events/christmas/')
  205. )
  206. self.assertEqual(christmas_page.full_url, 'http://localhost/events/christmas/')
  207. self.assertEqual(christmas_page.url, '/events/christmas/')
  208. self.assertEqual(christmas_page.relative_url(default_site), '/events/christmas/')
  209. self.assertEqual(christmas_page.get_site(), default_site)
  210. def test_page_with_no_url(self):
  211. root = Page.objects.get(url_path='/')
  212. default_site = Site.objects.get(is_default_site=True)
  213. self.assertEqual(root.get_url_parts(), None)
  214. self.assertEqual(root.full_url, None)
  215. self.assertEqual(root.url, None)
  216. self.assertEqual(root.relative_url(default_site), None)
  217. self.assertEqual(root.get_site(), None)
  218. def test_urls_with_multiple_sites(self):
  219. events_page = Page.objects.get(url_path='/home/events/')
  220. events_site = Site.objects.create(hostname='events.example.com', root_page=events_page)
  221. default_site = Site.objects.get(is_default_site=True)
  222. homepage = Page.objects.get(url_path='/home/')
  223. christmas_page = Page.objects.get(url_path='/home/events/christmas/')
  224. # with multiple sites, page.url will return full URLs to ensure that
  225. # they work across sites
  226. self.assertEqual(
  227. homepage.get_url_parts(),
  228. (default_site.id, 'http://localhost', '/')
  229. )
  230. self.assertEqual(homepage.full_url, 'http://localhost/')
  231. self.assertEqual(homepage.url, 'http://localhost/')
  232. self.assertEqual(homepage.relative_url(default_site), '/')
  233. self.assertEqual(homepage.relative_url(events_site), 'http://localhost/')
  234. self.assertEqual(homepage.get_site(), default_site)
  235. self.assertEqual(
  236. christmas_page.get_url_parts(),
  237. (events_site.id, 'http://events.example.com', '/christmas/')
  238. )
  239. self.assertEqual(christmas_page.full_url, 'http://events.example.com/christmas/')
  240. self.assertEqual(christmas_page.url, 'http://events.example.com/christmas/')
  241. self.assertEqual(christmas_page.relative_url(default_site), 'http://events.example.com/christmas/')
  242. self.assertEqual(christmas_page.relative_url(events_site), '/christmas/')
  243. self.assertEqual(christmas_page.get_site(), events_site)
  244. @override_settings(ROOT_URLCONF='wagtail.tests.non_root_urls')
  245. def test_urls_with_non_root_urlconf(self):
  246. default_site = Site.objects.get(is_default_site=True)
  247. homepage = Page.objects.get(url_path='/home/')
  248. christmas_page = Page.objects.get(url_path='/home/events/christmas/')
  249. # Basic installation only has one site configured, so page.url will return local URLs
  250. self.assertEqual(
  251. homepage.get_url_parts(),
  252. (default_site.id, 'http://localhost', '/site/')
  253. )
  254. self.assertEqual(homepage.full_url, 'http://localhost/site/')
  255. self.assertEqual(homepage.url, '/site/')
  256. self.assertEqual(homepage.relative_url(default_site), '/site/')
  257. self.assertEqual(homepage.get_site(), default_site)
  258. self.assertEqual(
  259. christmas_page.get_url_parts(),
  260. (default_site.id, 'http://localhost', '/site/events/christmas/')
  261. )
  262. self.assertEqual(christmas_page.full_url, 'http://localhost/site/events/christmas/')
  263. self.assertEqual(christmas_page.url, '/site/events/christmas/')
  264. self.assertEqual(christmas_page.relative_url(default_site), '/site/events/christmas/')
  265. self.assertEqual(christmas_page.get_site(), default_site)
  266. def test_request_routing(self):
  267. homepage = Page.objects.get(url_path='/home/')
  268. christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
  269. request = HttpRequest()
  270. request.path = '/events/christmas/'
  271. (found_page, args, kwargs) = homepage.route(request, ['events', 'christmas'])
  272. self.assertEqual(found_page, christmas_page)
  273. def test_request_serving(self):
  274. christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
  275. request = HttpRequest()
  276. request.user = AnonymousUser()
  277. request.site = Site.objects.first()
  278. response = christmas_page.serve(request)
  279. self.assertEqual(response.status_code, 200)
  280. self.assertEqual(response.context_data['self'], christmas_page)
  281. # confirm that the event_page.html template was used
  282. self.assertContains(response, '<h2>Event</h2>')
  283. def test_route_to_unknown_page_returns_404(self):
  284. homepage = Page.objects.get(url_path='/home/')
  285. request = HttpRequest()
  286. request.path = '/events/quinquagesima/'
  287. with self.assertRaises(Http404):
  288. homepage.route(request, ['events', 'quinquagesima'])
  289. def test_route_to_unpublished_page_returns_404(self):
  290. homepage = Page.objects.get(url_path='/home/')
  291. request = HttpRequest()
  292. request.path = '/events/tentative-unpublished-event/'
  293. with self.assertRaises(Http404):
  294. homepage.route(request, ['events', 'tentative-unpublished-event'])
  295. # Override CACHES so we don't generate any cache-related SQL queries (tests use DatabaseCache
  296. # otherwise) and so cache.get will always return None.
  297. @override_settings(CACHES={'default': {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}})
  298. def test_request_scope_site_root_paths_cache(self):
  299. homepage = Page.objects.get(url_path='/home/')
  300. christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
  301. # without a request, get_url should only issue 1 SQL query
  302. with self.assertNumQueries(1):
  303. self.assertEqual(homepage.get_url(), '/')
  304. # subsequent calls with the same page should generate no SQL queries
  305. with self.assertNumQueries(0):
  306. self.assertEqual(homepage.get_url(), '/')
  307. # subsequent calls with a different page will still generate 1 SQL query
  308. with self.assertNumQueries(1):
  309. self.assertEqual(christmas_page.get_url(), '/events/christmas/')
  310. # with a request, the first call to get_url should issue 1 SQL query
  311. request = HttpRequest()
  312. with self.assertNumQueries(1):
  313. self.assertEqual(homepage.get_url(request=request), '/')
  314. # subsequent calls should issue no SQL queries
  315. with self.assertNumQueries(0):
  316. self.assertEqual(homepage.get_url(request=request), '/')
  317. # even if called on a different page
  318. with self.assertNumQueries(0):
  319. self.assertEqual(christmas_page.get_url(request=request), '/events/christmas/')
  320. class TestServeView(TestCase):
  321. fixtures = ['test.json']
  322. def setUp(self):
  323. # Explicitly clear the cache of site root paths. Normally this would be kept
  324. # in sync by the Site.save logic, but this is bypassed when the database is
  325. # rolled back between tests using transactions.
  326. from django.core.cache import cache
  327. cache.delete('wagtail_site_root_paths')
  328. # also need to clear urlresolver caches before/after tests, because we override
  329. # ROOT_URLCONF in some tests here
  330. from django.urls import clear_url_caches
  331. clear_url_caches()
  332. def tearDown(self):
  333. from django.urls import clear_url_caches
  334. clear_url_caches()
  335. def test_serve(self):
  336. response = self.client.get('/events/christmas/')
  337. self.assertEqual(response.status_code, 200)
  338. self.assertEqual(response.templates[0].name, 'tests/event_page.html')
  339. christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
  340. self.assertEqual(response.context['self'], christmas_page)
  341. self.assertContains(response, '<h1>Christmas</h1>')
  342. self.assertContains(response, '<h2>Event</h2>')
  343. @override_settings(ROOT_URLCONF='wagtail.tests.non_root_urls')
  344. def test_serve_with_non_root_urls(self):
  345. response = self.client.get('/site/events/christmas/')
  346. self.assertEqual(response.status_code, 200)
  347. self.assertEqual(response.templates[0].name, 'tests/event_page.html')
  348. christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
  349. self.assertEqual(response.context['self'], christmas_page)
  350. self.assertContains(response, '<h1>Christmas</h1>')
  351. self.assertContains(response, '<h2>Event</h2>')
  352. def test_serve_unknown_page_returns_404(self):
  353. response = self.client.get('/events/quinquagesima/')
  354. self.assertEqual(response.status_code, 404)
  355. def test_serve_unpublished_page_returns_404(self):
  356. response = self.client.get('/events/tentative-unpublished-event/')
  357. self.assertEqual(response.status_code, 404)
  358. @override_settings(ALLOWED_HOSTS=['localhost', 'events.example.com'])
  359. def test_serve_with_multiple_sites(self):
  360. events_page = Page.objects.get(url_path='/home/events/')
  361. Site.objects.create(hostname='events.example.com', root_page=events_page)
  362. response = self.client.get('/christmas/', HTTP_HOST='events.example.com')
  363. self.assertEqual(response.status_code, 200)
  364. self.assertEqual(response.templates[0].name, 'tests/event_page.html')
  365. christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
  366. self.assertEqual(response.context['self'], christmas_page)
  367. self.assertContains(response, '<h1>Christmas</h1>')
  368. self.assertContains(response, '<h2>Event</h2>')
  369. # same request to the default host should return a 404
  370. c = Client()
  371. response = c.get('/christmas/', HTTP_HOST='localhost')
  372. self.assertEqual(response.status_code, 404)
  373. def test_serve_with_custom_context(self):
  374. response = self.client.get('/events/')
  375. self.assertEqual(response.status_code, 200)
  376. # should render the whole page
  377. self.assertContains(response, '<h1>Events</h1>')
  378. # response should contain data from the custom 'events' context variable
  379. self.assertContains(response, '<a href="/events/christmas/">Christmas</a>')
  380. def test_ajax_response(self):
  381. response = self.client.get('/events/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
  382. self.assertEqual(response.status_code, 200)
  383. # should only render the content of includes/event_listing.html, not the whole page
  384. self.assertNotContains(response, '<h1>Events</h1>')
  385. self.assertContains(response, '<a href="/events/christmas/">Christmas</a>')
  386. def test_before_serve_hook(self):
  387. response = self.client.get('/events/', HTTP_USER_AGENT='GoogleBot')
  388. self.assertContains(response, 'bad googlebot no cookie')
  389. class TestStaticSitePaths(TestCase):
  390. def setUp(self):
  391. self.root_page = Page.objects.get(id=1)
  392. # For simple tests
  393. self.home_page = self.root_page.add_child(
  394. instance=SimplePage(title="Homepage", slug="home2", content="hello")
  395. )
  396. self.about_page = self.home_page.add_child(
  397. instance=SimplePage(title="About us", slug="about", content="hello")
  398. )
  399. self.contact_page = self.home_page.add_child(
  400. instance=SimplePage(title="Contact", slug="contact", content="hello")
  401. )
  402. # For custom tests
  403. self.event_index = self.root_page.add_child(instance=EventIndex(title="Events", slug="events"))
  404. for i in range(20):
  405. self.event_index.add_child(instance=EventPage(
  406. title="Event " + str(i), slug="event" + str(i),
  407. location='the moon', audience='public',
  408. cost='free', date_from='2001-01-01',
  409. ))
  410. def test_local_static_site_paths(self):
  411. paths = list(self.about_page.get_static_site_paths())
  412. self.assertEqual(paths, ['/'])
  413. def test_child_static_site_paths(self):
  414. paths = list(self.home_page.get_static_site_paths())
  415. self.assertEqual(paths, ['/', '/about/', '/contact/'])
  416. def test_custom_static_site_paths(self):
  417. paths = list(self.event_index.get_static_site_paths())
  418. # Event index path
  419. expected_paths = ['/']
  420. # One path for each page of results
  421. expected_paths.extend(['/' + str(i + 1) + '/' for i in range(5)])
  422. # One path for each event page
  423. expected_paths.extend(['/event' + str(i) + '/' for i in range(20)])
  424. paths.sort()
  425. expected_paths.sort()
  426. self.assertEqual(paths, expected_paths)
  427. class TestMovePage(TestCase):
  428. fixtures = ['test.json']
  429. def test_move_page(self):
  430. about_us_page = SimplePage.objects.get(url_path='/home/about-us/')
  431. events_index = EventIndex.objects.get(url_path='/home/events/')
  432. events_index.move(about_us_page, pos='last-child')
  433. # re-fetch events index to confirm that db fields have been updated
  434. events_index = EventIndex.objects.get(id=events_index.id)
  435. self.assertEqual(events_index.url_path, '/home/about-us/events/')
  436. self.assertEqual(events_index.depth, 4)
  437. self.assertEqual(events_index.get_parent().id, about_us_page.id)
  438. # children of events_index should also have been updated
  439. christmas = events_index.get_children().get(slug='christmas')
  440. self.assertEqual(christmas.depth, 5)
  441. self.assertEqual(christmas.url_path, '/home/about-us/events/christmas/')
  442. class TestPrevNextSiblings(TestCase):
  443. fixtures = ['test.json']
  444. def test_get_next_siblings(self):
  445. christmas_event = Page.objects.get(url_path='/home/events/christmas/')
  446. self.assertTrue(christmas_event.get_next_siblings().filter(url_path='/home/events/final-event/').exists())
  447. def test_get_next_siblings_inclusive(self):
  448. christmas_event = Page.objects.get(url_path='/home/events/christmas/')
  449. # First element must always be the current page
  450. self.assertEqual(christmas_event.get_next_siblings(inclusive=True).first(), christmas_event)
  451. def test_get_prev_siblings(self):
  452. final_event = Page.objects.get(url_path='/home/events/final-event/')
  453. self.assertTrue(final_event.get_prev_siblings().filter(url_path='/home/events/christmas/').exists())
  454. # First element must always be the current page
  455. self.assertEqual(final_event.get_prev_siblings(inclusive=True).first(), final_event)
  456. class TestLiveRevision(TestCase):
  457. fixtures = ['test.json']
  458. @freeze_time("2017-01-01 12:00:00")
  459. def test_publish_method_will_set_live_revision(self):
  460. page = Page.objects.get(id=2)
  461. revision = page.save_revision()
  462. revision.publish()
  463. page.refresh_from_db()
  464. self.assertEqual(page.live_revision, revision)
  465. self.assertEqual(page.last_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
  466. # first_published_at should not change
  467. self.assertEqual(page.first_published_at, datetime.datetime(2014, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
  468. @freeze_time("2017-01-01 12:00:00")
  469. def test_unpublish_method_will_clean_live_revision(self):
  470. page = Page.objects.get(id=2)
  471. revision = page.save_revision()
  472. revision.publish()
  473. page.refresh_from_db()
  474. page.unpublish()
  475. page.refresh_from_db()
  476. self.assertIsNone(page.live_revision)
  477. # first_published_at / last_published_at should remain unchanged on unpublish
  478. self.assertEqual(page.first_published_at, datetime.datetime(2014, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
  479. self.assertEqual(page.last_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
  480. @freeze_time("2017-01-01 12:00:00")
  481. def test_copy_method_with_keep_live_will_update_live_revision(self):
  482. about_us = SimplePage.objects.get(url_path='/home/about-us/')
  483. revision = about_us.save_revision()
  484. revision.publish()
  485. new_about_us = about_us.copy(keep_live=True, update_attrs={'title': "New about us", 'slug': 'new-about-us'})
  486. self.assertIsNotNone(new_about_us.live_revision)
  487. self.assertNotEqual(about_us.live_revision, new_about_us.live_revision)
  488. # first_published_at / last_published_at should reflect the current time,
  489. # not the source page's publish dates, since the copied page is being published
  490. # for the first time
  491. self.assertEqual(new_about_us.first_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
  492. self.assertEqual(new_about_us.last_published_at, datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
  493. def test_copy_method_without_keep_live_will_not_update_live_revision(self):
  494. about_us = SimplePage.objects.get(url_path='/home/about-us/')
  495. revision = about_us.save_revision()
  496. revision.publish()
  497. about_us.refresh_from_db()
  498. self.assertIsNotNone(about_us.live_revision)
  499. new_about_us = about_us.copy(keep_live=False, update_attrs={'title': "New about us", 'slug': 'new-about-us'})
  500. self.assertIsNone(new_about_us.live_revision)
  501. # first_published_at / last_published_at should be blank, because the copied article
  502. # has not been published
  503. self.assertIsNone(new_about_us.first_published_at)
  504. self.assertIsNone(new_about_us.last_published_at)
  505. @freeze_time("2017-01-01 12:00:00")
  506. def test_publish_with_future_go_live_does_not_set_live_revision(self):
  507. about_us = SimplePage.objects.get(url_path='/home/about-us/')
  508. about_us.go_live_at = datetime.datetime(2018, 1, 1, 12, 0, 0, tzinfo=pytz.utc)
  509. revision = about_us.save_revision()
  510. revision.publish()
  511. about_us.refresh_from_db()
  512. self.assertFalse(about_us.live)
  513. self.assertIsNone(about_us.live_revision)
  514. # first_published_at / last_published_at should remain unchanged
  515. self.assertEqual(about_us.first_published_at, datetime.datetime(2014, 1, 1, 12, 0, 0, tzinfo=pytz.utc))
  516. self.assertEqual(about_us.last_published_at, datetime.datetime(2014, 2, 1, 12, 0, 0, tzinfo=pytz.utc))
  517. class TestCopyPage(TestCase):
  518. fixtures = ['test.json']
  519. def test_copy_page_copies(self):
  520. about_us = SimplePage.objects.get(url_path='/home/about-us/')
  521. # Copy it
  522. new_about_us = about_us.copy(update_attrs={'title': "New about us", 'slug': 'new-about-us'})
  523. # Check that new_about_us is correct
  524. self.assertIsInstance(new_about_us, SimplePage)
  525. self.assertEqual(new_about_us.title, "New about us")
  526. self.assertEqual(new_about_us.slug, 'new-about-us')
  527. # Check that new_about_us is a different page
  528. self.assertNotEqual(about_us.id, new_about_us.id)
  529. # Check that the url path was updated
  530. self.assertEqual(new_about_us.url_path, '/home/new-about-us/')
  531. def test_copy_page_copies_child_objects(self):
  532. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  533. # Copy it
  534. new_christmas_event = christmas_event.copy(
  535. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
  536. )
  537. # Check that the speakers were copied
  538. self.assertEqual(new_christmas_event.speakers.count(), 1, "Child objects weren't copied")
  539. # Check that the speakers weren't removed from old page
  540. self.assertEqual(christmas_event.speakers.count(), 1, "Child objects were removed from the original page")
  541. # Check that advert placements were also copied (there's a gotcha here, since the advert_placements
  542. # relation is defined on Page, not EventPage)
  543. self.assertEqual(
  544. new_christmas_event.advert_placements.count(), 1, "Child objects defined on the superclass weren't copied"
  545. )
  546. self.assertEqual(
  547. christmas_event.advert_placements.count(),
  548. 1,
  549. "Child objects defined on the superclass were removed from the original page"
  550. )
  551. def test_copy_page_copies_revisions(self):
  552. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  553. christmas_event.save_revision()
  554. # Copy it
  555. new_christmas_event = christmas_event.copy(
  556. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
  557. )
  558. # Check that the revisions were copied
  559. # Copying creates a new revision so we're expecting the new page to have two revisions
  560. self.assertEqual(new_christmas_event.revisions.count(), 2)
  561. # Check that the revisions weren't removed from old page
  562. self.assertEqual(christmas_event.revisions.count(), 1, "Revisions were removed from the original page")
  563. # Check that the attributes were updated in the latest revision
  564. latest_revision = new_christmas_event.get_latest_revision_as_page()
  565. self.assertEqual(latest_revision.title, "New christmas event")
  566. self.assertEqual(latest_revision.slug, 'new-christmas-event')
  567. # get_latest_revision_as_page might bypass the revisions table if it determines
  568. # that there are no draft edits since publish - so retrieve it explicitly from the
  569. # revision data, to ensure it's been updated there too
  570. latest_revision = new_christmas_event.get_latest_revision().as_page_object()
  571. self.assertEqual(latest_revision.title, "New christmas event")
  572. self.assertEqual(latest_revision.slug, 'new-christmas-event')
  573. # Check that the ids within the revision were updated correctly
  574. new_revision = new_christmas_event.revisions.first()
  575. new_revision_content = json.loads(new_revision.content_json)
  576. self.assertEqual(new_revision_content['pk'], new_christmas_event.id)
  577. self.assertEqual(new_revision_content['speakers'][0]['page'], new_christmas_event.id)
  578. # Also, check that the child objects in the new revision are given new IDs
  579. old_speakers_ids = set(christmas_event.speakers.values_list('id', flat=True))
  580. new_speakers_ids = set(speaker['pk'] for speaker in new_revision_content['speakers'])
  581. self.assertFalse(
  582. old_speakers_ids.intersection(new_speakers_ids),
  583. "Child objects in revisions were not given a new primary key"
  584. )
  585. def test_copy_page_copies_revisions_and_doesnt_submit_for_moderation(self):
  586. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  587. christmas_event.save_revision(submitted_for_moderation=True)
  588. # Copy it
  589. new_christmas_event = christmas_event.copy(
  590. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
  591. )
  592. # Check that the old revision is still submitted for moderation
  593. self.assertTrue(christmas_event.revisions.order_by('created_at').first().submitted_for_moderation)
  594. # Check that the new revision is not submitted for moderation
  595. self.assertFalse(new_christmas_event.revisions.order_by('created_at').first().submitted_for_moderation)
  596. def test_copy_page_copies_revisions_and_doesnt_change_created_at(self):
  597. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  598. christmas_event.save_revision(submitted_for_moderation=True)
  599. # Set the created_at of the revision to a time in the past
  600. revision = christmas_event.get_latest_revision()
  601. revision.created_at = datetime.datetime(2014, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
  602. revision.save()
  603. # Copy it
  604. new_christmas_event = christmas_event.copy(
  605. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
  606. )
  607. # Check that the created_at time is the same
  608. christmas_event_created_at = christmas_event.revisions.order_by('created_at').first().created_at
  609. new_christmas_event_created_at = new_christmas_event.revisions.order_by('created_at').first().created_at
  610. self.assertEqual(christmas_event_created_at, new_christmas_event_created_at)
  611. def test_copy_page_copies_revisions_and_doesnt_schedule(self):
  612. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  613. christmas_event.save_revision(approved_go_live_at=datetime.datetime(2014, 9, 16, 9, 12, 00, tzinfo=pytz.utc))
  614. # Copy it
  615. new_christmas_event = christmas_event.copy(
  616. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
  617. )
  618. # Check that the old revision is still scheduled
  619. self.assertEqual(
  620. christmas_event.revisions.order_by('created_at').first().approved_go_live_at,
  621. datetime.datetime(2014, 9, 16, 9, 12, 00, tzinfo=pytz.utc)
  622. )
  623. # Check that the new revision is not scheduled
  624. self.assertEqual(new_christmas_event.revisions.order_by('created_at').first().approved_go_live_at, None)
  625. def test_copy_page_doesnt_copy_revisions_if_told_not_to_do_so(self):
  626. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  627. christmas_event.save_revision()
  628. # Copy it
  629. new_christmas_event = christmas_event.copy(
  630. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}, copy_revisions=False
  631. )
  632. # Check that the revisions weren't copied
  633. # Copying creates a new revision so we're expecting the new page to have one revision
  634. self.assertEqual(new_christmas_event.revisions.count(), 1)
  635. # Check that the revisions weren't removed from old page
  636. self.assertEqual(christmas_event.revisions.count(), 1, "Revisions were removed from the original page")
  637. def test_copy_page_copies_child_objects_with_nonspecific_class(self):
  638. # Get chrismas page as Page instead of EventPage
  639. christmas_event = Page.objects.get(url_path='/home/events/christmas/')
  640. # Copy it
  641. new_christmas_event = christmas_event.copy(
  642. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'}
  643. )
  644. # Check that the type of the new page is correct
  645. self.assertIsInstance(new_christmas_event, EventPage)
  646. # Check that the speakers were copied
  647. self.assertEqual(new_christmas_event.speakers.count(), 1, "Child objects weren't copied")
  648. def test_copy_page_copies_recursively(self):
  649. events_index = EventIndex.objects.get(url_path='/home/events/')
  650. # Copy it
  651. new_events_index = events_index.copy(
  652. recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}
  653. )
  654. # Get christmas event
  655. old_christmas_event = events_index.get_children().filter(slug='christmas').first()
  656. new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
  657. # Check that the event exists in both places
  658. self.assertNotEqual(new_christmas_event, None, "Child pages weren't copied")
  659. self.assertNotEqual(old_christmas_event, None, "Child pages were removed from original page")
  660. # Check that the url path was updated
  661. self.assertEqual(new_christmas_event.url_path, '/home/new-events-index/christmas/')
  662. def test_copy_page_copies_recursively_with_child_objects(self):
  663. events_index = EventIndex.objects.get(url_path='/home/events/')
  664. # Copy it
  665. new_events_index = events_index.copy(
  666. recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}
  667. )
  668. # Get christmas event
  669. old_christmas_event = events_index.get_children().filter(slug='christmas').first()
  670. new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
  671. # Check that the speakers were copied
  672. self.assertEqual(new_christmas_event.specific.speakers.count(), 1, "Child objects weren't copied")
  673. # Check that the speakers weren't removed from old page
  674. self.assertEqual(
  675. old_christmas_event.specific.speakers.count(), 1, "Child objects were removed from the original page"
  676. )
  677. def test_copy_page_copies_recursively_with_revisions(self):
  678. events_index = EventIndex.objects.get(url_path='/home/events/')
  679. old_christmas_event = events_index.get_children().filter(slug='christmas').first().specific
  680. old_christmas_event.save_revision()
  681. # Copy it
  682. new_events_index = events_index.copy(
  683. recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}
  684. )
  685. # Get christmas event
  686. new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
  687. # Check that the revisions were copied
  688. # Copying creates a new revision so we're expecting the new page to have two revisions
  689. self.assertEqual(new_christmas_event.specific.revisions.count(), 2)
  690. # Check that the revisions weren't removed from old page
  691. self.assertEqual(
  692. old_christmas_event.specific.revisions.count(), 1, "Revisions were removed from the original page"
  693. )
  694. def test_copy_page_copies_recursively_but_doesnt_copy_revisions_if_told_not_to_do_so(self):
  695. events_index = EventIndex.objects.get(url_path='/home/events/')
  696. old_christmas_event = events_index.get_children().filter(slug='christmas').first()
  697. old_christmas_event.save_revision()
  698. # Copy it
  699. new_events_index = events_index.copy(
  700. recursive=True,
  701. update_attrs={'title': "New events index", 'slug': 'new-events-index'},
  702. copy_revisions=False
  703. )
  704. # Get christmas event
  705. new_christmas_event = new_events_index.get_children().filter(slug='christmas').first()
  706. # Check that the revisions weren't copied
  707. # Copying creates a new revision so we're expecting the new page to have one revision
  708. self.assertEqual(new_christmas_event.specific.revisions.count(), 1)
  709. # Check that the revisions weren't removed from old page
  710. self.assertEqual(
  711. old_christmas_event.specific.revisions.count(), 1, "Revisions were removed from the original page"
  712. )
  713. def test_copy_page_copies_recursively_to_the_same_tree(self):
  714. events_index = EventIndex.objects.get(url_path='/home/events/')
  715. old_christmas_event = events_index.get_children().filter(slug='christmas').first().specific
  716. old_christmas_event.save_revision()
  717. with self.assertRaises(Exception) as exception:
  718. events_index.copy(
  719. recursive=True, update_attrs={'title': "New events index", 'slug': 'new-events-index'}, to=events_index
  720. )
  721. self.assertEqual(str(exception.exception), "You cannot copy a tree branch recursively into itself")
  722. def test_copy_page_updates_user(self):
  723. event_moderator = get_user_model().objects.get(username='eventmoderator')
  724. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  725. christmas_event.save_revision()
  726. # Copy it
  727. new_christmas_event = christmas_event.copy(
  728. update_attrs={'title': "New christmas event", 'slug': 'new-christmas-event'},
  729. user=event_moderator,
  730. )
  731. # Check that the owner has been updated
  732. self.assertEqual(new_christmas_event.owner, event_moderator)
  733. # Check that the user on the last revision is correct
  734. self.assertEqual(new_christmas_event.get_latest_revision().user, event_moderator)
  735. def test_copy_multi_table_inheritance(self):
  736. saint_patrick_event = SingleEventPage.objects.get(url_path='/home/events/saint-patrick/')
  737. # Copy it
  738. new_saint_patrick_event = saint_patrick_event.copy(update_attrs={'slug': 'new-saint-patrick'})
  739. # Check that new_saint_patrick_event is correct
  740. self.assertIsInstance(new_saint_patrick_event, SingleEventPage)
  741. self.assertEqual(new_saint_patrick_event.excerpt, saint_patrick_event.excerpt)
  742. # Check that new_saint_patrick_event is a different page, including parents from both EventPage and Page
  743. self.assertNotEqual(saint_patrick_event.id, new_saint_patrick_event.id)
  744. self.assertNotEqual(saint_patrick_event.eventpage_ptr.id, new_saint_patrick_event.eventpage_ptr.id)
  745. self.assertNotEqual(
  746. saint_patrick_event.eventpage_ptr.page_ptr.id,
  747. new_saint_patrick_event.eventpage_ptr.page_ptr.id
  748. )
  749. # Check that the url path was updated
  750. self.assertEqual(new_saint_patrick_event.url_path, '/home/events/new-saint-patrick/')
  751. # Check that both parent instance exists
  752. self.assertIsInstance(EventPage.objects.get(id=new_saint_patrick_event.id), EventPage)
  753. self.assertIsInstance(Page.objects.get(id=new_saint_patrick_event.id), Page)
  754. def test_copy_page_copies_tags(self):
  755. # create and publish a TaggedPage under Events
  756. event_index = Page.objects.get(url_path='/home/events/')
  757. tagged_page = TaggedPage(title='My tagged page', slug='my-tagged-page')
  758. tagged_page.tags.add('wagtail', 'bird')
  759. event_index.add_child(instance=tagged_page)
  760. tagged_page.save_revision().publish()
  761. old_tagged_item_ids = [item.id for item in tagged_page.tagged_items.all()]
  762. # there should be two items here, with defined (truthy) IDs
  763. self.assertEqual(len(old_tagged_item_ids), 2)
  764. self.assertTrue(all(old_tagged_item_ids))
  765. # copy to underneath homepage
  766. homepage = Page.objects.get(url_path='/home/')
  767. new_tagged_page = tagged_page.copy(to=homepage)
  768. self.assertNotEqual(tagged_page.id, new_tagged_page.id)
  769. # new page should also have two tags
  770. new_tagged_item_ids = [item.id for item in new_tagged_page.tagged_items.all()]
  771. self.assertEqual(len(new_tagged_item_ids), 2)
  772. self.assertTrue(all(new_tagged_item_ids))
  773. # new tagged_item IDs should differ from old ones
  774. self.assertTrue(all([
  775. item_id not in old_tagged_item_ids
  776. for item_id in new_tagged_item_ids
  777. ]))
  778. def test_copy_page_with_m2m_relations(self):
  779. # create and publish a ManyToManyBlogPage under Events
  780. event_index = Page.objects.get(url_path='/home/events/')
  781. category = BlogCategory.objects.create(name='Birds')
  782. advert = Advert.objects.create(url='http://www.heinz.com/', text="beanz meanz heinz")
  783. blog_page = ManyToManyBlogPage(title='My blog page', slug='my-blog-page')
  784. event_index.add_child(instance=blog_page)
  785. blog_page.adverts.add(advert)
  786. BlogCategoryBlogPage.objects.create(category=category, page=blog_page)
  787. blog_page.save_revision().publish()
  788. # copy to underneath homepage
  789. homepage = Page.objects.get(url_path='/home/')
  790. new_blog_page = blog_page.copy(to=homepage)
  791. # M2M relations are not formally supported, so for now we're only interested in
  792. # the copy operation as a whole succeeding, rather than the child objects being copied
  793. self.assertNotEqual(blog_page.id, new_blog_page.id)
  794. def test_copy_page_with_generic_foreign_key(self):
  795. # create and publish a GenericSnippetPage under Events
  796. event_index = Page.objects.get(url_path='/home/events/')
  797. advert = Advert.objects.create(url='http://www.heinz.com/', text="beanz meanz heinz")
  798. page = GenericSnippetPage(title='My snippet page', slug='my-snippet-page')
  799. page.snippet_content_object = advert
  800. event_index.add_child(instance=page)
  801. page.save_revision().publish()
  802. # copy to underneath homepage
  803. homepage = Page.objects.get(url_path='/home/')
  804. new_page = page.copy(to=homepage)
  805. self.assertNotEqual(page.id, new_page.id)
  806. self.assertEqual(new_page.snippet_content_object, advert)
  807. def test_copy_page_with_o2o_relation(self):
  808. event_index = Page.objects.get(url_path='/home/events/')
  809. page = OneToOnePage(title='My page', slug='my-page')
  810. event_index.add_child(instance=page)
  811. homepage = Page.objects.get(url_path='/home/')
  812. new_page = page.copy(to=homepage)
  813. self.assertNotEqual(page.id, new_page.id)
  814. def test_copy_page_with_additional_excluded_fields(self):
  815. homepage = Page.objects.get(url_path='/home/')
  816. page = PageWithExcludedCopyField(
  817. title='Discovery',
  818. slug='disco',
  819. content='NCC-1031',
  820. special_field='Context is for Kings')
  821. new_page = page.copy(to=homepage)
  822. self.assertEqual(page.title, new_page.title)
  823. self.assertNotEqual(page.id, new_page.id)
  824. self.assertNotEqual(page.path, new_page.path)
  825. # special_field is in the list to be excluded
  826. self.assertNotEqual(page.special_field, new_page.special_field)
  827. class TestSubpageTypeBusinessRules(TestCase, WagtailTestUtils):
  828. def test_allowed_subpage_models(self):
  829. # SimplePage does not define any restrictions on subpage types
  830. # SimplePage is a valid subpage of SimplePage
  831. self.assertIn(SimplePage, SimplePage.allowed_subpage_models())
  832. # BusinessIndex is a valid subpage of SimplePage
  833. self.assertIn(BusinessIndex, SimplePage.allowed_subpage_models())
  834. # BusinessSubIndex is not valid, because it explicitly omits SimplePage from parent_page_types
  835. self.assertNotIn(BusinessSubIndex, SimplePage.allowed_subpage_models())
  836. # BusinessChild has an empty subpage_types list, so does not allow anything
  837. self.assertNotIn(SimplePage, BusinessChild.allowed_subpage_models())
  838. self.assertNotIn(BusinessIndex, BusinessChild.allowed_subpage_models())
  839. self.assertNotIn(BusinessSubIndex, BusinessChild.allowed_subpage_models())
  840. # BusinessSubIndex only allows BusinessChild as subpage type
  841. self.assertNotIn(SimplePage, BusinessSubIndex.allowed_subpage_models())
  842. self.assertIn(BusinessChild, BusinessSubIndex.allowed_subpage_models())
  843. def test_allowed_parent_page_models(self):
  844. # SimplePage does not define any restrictions on parent page types
  845. # SimplePage is a valid parent page of SimplePage
  846. self.assertIn(SimplePage, SimplePage.allowed_parent_page_models())
  847. # BusinessChild cannot be a parent of anything
  848. self.assertNotIn(BusinessChild, SimplePage.allowed_parent_page_models())
  849. # BusinessNowherePage does not allow anything as a parent
  850. self.assertNotIn(SimplePage, BusinessNowherePage.allowed_parent_page_models())
  851. self.assertNotIn(StandardIndex, BusinessNowherePage.allowed_parent_page_models())
  852. # BusinessSubIndex only allows BusinessIndex as a parent
  853. self.assertNotIn(SimplePage, BusinessSubIndex.allowed_parent_page_models())
  854. self.assertIn(BusinessIndex, BusinessSubIndex.allowed_parent_page_models())
  855. def test_can_exist_under(self):
  856. self.assertTrue(SimplePage.can_exist_under(SimplePage()))
  857. # StandardIndex should only be allowed under a Page
  858. self.assertTrue(StandardIndex.can_exist_under(Page()))
  859. self.assertFalse(StandardIndex.can_exist_under(SimplePage()))
  860. # The Business pages are quite restrictive in their structure
  861. self.assertTrue(BusinessSubIndex.can_exist_under(BusinessIndex()))
  862. self.assertTrue(BusinessChild.can_exist_under(BusinessIndex()))
  863. self.assertTrue(BusinessChild.can_exist_under(BusinessSubIndex()))
  864. self.assertFalse(BusinessSubIndex.can_exist_under(SimplePage()))
  865. self.assertFalse(BusinessSubIndex.can_exist_under(BusinessSubIndex()))
  866. self.assertFalse(BusinessChild.can_exist_under(SimplePage()))
  867. def test_can_create_at(self):
  868. # Pages are not `is_creatable`, and should not be creatable
  869. self.assertFalse(Page.can_create_at(Page()))
  870. # SimplePage can be created under a simple page
  871. self.assertTrue(SimplePage.can_create_at(SimplePage()))
  872. # StandardIndex can be created under a Page, but not a SimplePage
  873. self.assertTrue(StandardIndex.can_create_at(Page()))
  874. self.assertFalse(StandardIndex.can_create_at(SimplePage()))
  875. # The Business pages are quite restrictive in their structure
  876. self.assertTrue(BusinessSubIndex.can_create_at(BusinessIndex()))
  877. self.assertTrue(BusinessChild.can_create_at(BusinessIndex()))
  878. self.assertTrue(BusinessChild.can_create_at(BusinessSubIndex()))
  879. self.assertFalse(BusinessChild.can_create_at(SimplePage()))
  880. self.assertFalse(BusinessSubIndex.can_create_at(SimplePage()))
  881. def test_can_move_to(self):
  882. self.assertTrue(SimplePage().can_move_to(SimplePage()))
  883. # StandardIndex should only be allowed under a Page
  884. self.assertTrue(StandardIndex().can_move_to(Page()))
  885. self.assertFalse(StandardIndex().can_move_to(SimplePage()))
  886. # The Business pages are quite restrictive in their structure
  887. self.assertTrue(BusinessSubIndex().can_move_to(BusinessIndex()))
  888. self.assertTrue(BusinessChild().can_move_to(BusinessIndex()))
  889. self.assertTrue(BusinessChild().can_move_to(BusinessSubIndex()))
  890. self.assertFalse(BusinessChild().can_move_to(SimplePage()))
  891. self.assertFalse(BusinessSubIndex().can_move_to(SimplePage()))
  892. def test_singleton_page_creation(self):
  893. root_page = Page.objects.get(url_path='/home/')
  894. # A single singleton page should be creatable
  895. self.assertTrue(SingletonPage.can_create_at(root_page))
  896. # Create a singleton page
  897. root_page.add_child(instance=SingletonPage(
  898. title='singleton', slug='singleton'))
  899. # A second singleton page should not be creatable
  900. self.assertFalse(SingletonPage.can_create_at(root_page))
  901. class TestIssue735(TestCase):
  902. """
  903. Issue 735 reports that URL paths of child pages are not
  904. updated correctly when slugs of parent pages are updated
  905. """
  906. fixtures = ['test.json']
  907. def test_child_urls_updated_on_parent_publish(self):
  908. event_index = Page.objects.get(url_path='/home/events/')
  909. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  910. # Change the event index slug and publish it
  911. event_index.slug = 'old-events'
  912. event_index.save_revision().publish()
  913. # Check that the christmas events url path updated correctly
  914. new_christmas_event = EventPage.objects.get(id=christmas_event.id)
  915. self.assertEqual(new_christmas_event.url_path, '/home/old-events/christmas/')
  916. class TestIssue756(TestCase):
  917. """
  918. Issue 756 reports that the latest_revision_created_at
  919. field was getting clobbered whenever a revision was published
  920. """
  921. def test_publish_revision_doesnt_remove_latest_revision_created_at(self):
  922. # Create a revision
  923. revision = Page.objects.get(id=1).save_revision()
  924. # Check that latest_revision_created_at is set
  925. self.assertIsNotNone(Page.objects.get(id=1).latest_revision_created_at)
  926. # Publish the revision
  927. revision.publish()
  928. # Check that latest_revision_created_at is still set
  929. self.assertIsNotNone(Page.objects.get(id=1).latest_revision_created_at)
  930. class TestIssue1216(TestCase):
  931. """
  932. Test that url paths greater than 255 characters are supported
  933. """
  934. fixtures = ['test.json']
  935. def test_url_path_can_exceed_255_characters(self):
  936. event_index = Page.objects.get(url_path='/home/events/')
  937. christmas_event = EventPage.objects.get(url_path='/home/events/christmas/')
  938. # Change the christmas_event slug first - this way, we test that the process for
  939. # updating child url paths also handles >255 character paths correctly
  940. new_christmas_slug = "christmas-%s-christmas" % ("0123456789" * 20)
  941. christmas_event.slug = new_christmas_slug
  942. christmas_event.save_revision().publish()
  943. # Change the event index slug and publish it
  944. new_event_index_slug = "events-%s-events" % ("0123456789" * 20)
  945. event_index.slug = new_event_index_slug
  946. event_index.save_revision().publish()
  947. # Check that the url path updated correctly
  948. new_christmas_event = EventPage.objects.get(id=christmas_event.id)
  949. expected_url_path = "/home/%s/%s/" % (new_event_index_slug, new_christmas_slug)
  950. self.assertEqual(new_christmas_event.url_path, expected_url_path)
  951. class TestIsCreatable(TestCase):
  952. def test_is_creatable_default(self):
  953. """By default, pages should be creatable"""
  954. self.assertTrue(SimplePage.is_creatable)
  955. self.assertIn(SimplePage, get_page_models())
  956. def test_is_creatable_false(self):
  957. """Page types should be able to disable their creation"""
  958. self.assertFalse(MTIBasePage.is_creatable)
  959. # non-creatable pages should still appear in the get_page_models list
  960. self.assertIn(MTIBasePage, get_page_models())
  961. def test_is_creatable_not_inherited(self):
  962. """
  963. is_creatable should not be inherited in the normal manner, and should
  964. default to True unless set otherwise
  965. """
  966. self.assertTrue(MTIChildPage.is_creatable)
  967. self.assertIn(MTIChildPage, get_page_models())
  968. def test_abstract_pages(self):
  969. """
  970. Abstract models should not be creatable
  971. """
  972. self.assertFalse(AbstractPage.is_creatable)
  973. self.assertNotIn(AbstractPage, get_page_models())
  974. class TestDeferredPageClasses(TestCase):
  975. def test_deferred_page_classes_are_not_registered(self):
  976. """
  977. In Django <1.10, a call to `defer` such as `SimplePage.objects.defer('content')`
  978. will dynamically create a subclass of SimplePage. Ensure that these subclasses
  979. are not registered in the get_page_models() list
  980. """
  981. list(SimplePage.objects.defer('content'))
  982. simplepage_subclasses = [cls for cls in get_page_models() if issubclass(cls, SimplePage)]
  983. self.assertEqual(simplepage_subclasses, [SimplePage])
  984. class TestPageManager(TestCase):
  985. def test_page_manager(self):
  986. """
  987. Assert that the Page class uses PageManager
  988. """
  989. self.assertIs(type(Page.objects), PageManager)
  990. def test_page_subclass_manager(self):
  991. """
  992. Assert that Page subclasses get a PageManager without having to do
  993. anything special. MTI subclasses do *not* inherit their parents Manager
  994. by default.
  995. """
  996. self.assertIs(type(SimplePage.objects), PageManager)
  997. def test_custom_page_manager(self):
  998. """
  999. Subclasses should be able to override their default Manager, and
  1000. Wagtail should respect this. It is up to the developer to ensure their
  1001. custom Manager inherits from PageManager.
  1002. """
  1003. self.assertIs(type(CustomManagerPage.objects), CustomManager)
  1004. def test_custom_page_queryset(self):
  1005. """
  1006. Managers that are constructed from a custom PageQuerySet
  1007. (via PageManager.from_queryset(CustomPageQuerySet)) should return
  1008. querysets of that type
  1009. """
  1010. self.assertIs(type(CustomManagerPage.objects.all()), CustomPageQuerySet)
  1011. self.assertIs(type(CustomManagerPage.objects.about_spam()), CustomPageQuerySet)
  1012. self.assertIs(type(CustomManagerPage.objects.all().about_spam()), CustomPageQuerySet)
  1013. self.assertIs(type(CustomManagerPage.objects.about_spam().all()), CustomPageQuerySet)
  1014. def test_abstract_base_page_manager(self):
  1015. """
  1016. Abstract base classes should be able to override their default Manager,
  1017. and Wagtail should respect this. It is up to the developer to ensure
  1018. their custom Manager inherits from PageManager.
  1019. """
  1020. self.assertIs(type(MyCustomPage.objects), CustomManager)
  1021. class TestIssue2024(TestCase):
  1022. """
  1023. This tests that deleting a content type can't delete any Page objects.
  1024. """
  1025. fixtures = ['test.json']
  1026. def test_delete_content_type(self):
  1027. event_index = Page.objects.get(url_path='/home/events/')
  1028. # Delete the content type
  1029. event_index_content_type = event_index.content_type
  1030. event_index_content_type.delete()
  1031. # Fetch the page again, it should still exist
  1032. event_index = Page.objects.get(url_path='/home/events/')
  1033. # Check that the content_type changed to Page
  1034. self.assertEqual(event_index.content_type, ContentType.objects.get_for_model(Page))
  1035. @override_settings(ALLOWED_HOSTS=['localhost'])
  1036. class TestDummyRequest(TestCase):
  1037. fixtures = ['test.json']
  1038. def test_dummy_request_for_accessible_page(self):
  1039. event_index = Page.objects.get(url_path='/home/events/')
  1040. request = event_index.dummy_request()
  1041. # request should have the correct path and hostname for this page
  1042. self.assertEqual(request.path, '/events/')
  1043. self.assertEqual(request.META['HTTP_HOST'], 'localhost')
  1044. # check other env vars required by the WSGI spec
  1045. self.assertEqual(request.META['REQUEST_METHOD'], 'GET')
  1046. self.assertEqual(request.META['SCRIPT_NAME'], '')
  1047. self.assertEqual(request.META['PATH_INFO'], '/events/')
  1048. self.assertEqual(request.META['SERVER_NAME'], 'localhost')
  1049. self.assertEqual(request.META['SERVER_PORT'], 80)
  1050. self.assertEqual(request.META['SERVER_PROTOCOL'], 'HTTP/1.1')
  1051. self.assertEqual(request.META['wsgi.version'], (1, 0))
  1052. self.assertEqual(request.META['wsgi.url_scheme'], 'http')
  1053. self.assertIn('wsgi.input', request.META)
  1054. self.assertIn('wsgi.errors', request.META)
  1055. self.assertIn('wsgi.multithread', request.META)
  1056. self.assertIn('wsgi.multiprocess', request.META)
  1057. self.assertIn('wsgi.run_once', request.META)
  1058. def test_dummy_request_for_accessible_page_with_original_request(self):
  1059. event_index = Page.objects.get(url_path='/home/events/')
  1060. original_headers = {
  1061. 'REMOTE_ADDR': '192.168.0.1',
  1062. 'HTTP_X_FORWARDED_FOR': '192.168.0.2,192.168.0.3',
  1063. 'HTTP_COOKIE': "test=1;blah=2",
  1064. 'HTTP_USER_AGENT': "Test Agent",
  1065. }
  1066. factory = RequestFactory(**original_headers)
  1067. original_request = factory.get('/home/events/')
  1068. request = event_index.dummy_request(original_request)
  1069. # request should have the all the special headers we set in original_request
  1070. self.assertEqual(request.META['REMOTE_ADDR'], original_request.META['REMOTE_ADDR'])
  1071. self.assertEqual(request.META['HTTP_X_FORWARDED_FOR'], original_request.META['HTTP_X_FORWARDED_FOR'])
  1072. self.assertEqual(request.META['HTTP_COOKIE'], original_request.META['HTTP_COOKIE'])
  1073. self.assertEqual(request.META['HTTP_USER_AGENT'], original_request.META['HTTP_USER_AGENT'])
  1074. # check other env vars required by the WSGI spec
  1075. self.assertEqual(request.META['REQUEST_METHOD'], 'GET')
  1076. self.assertEqual(request.META['SCRIPT_NAME'], '')
  1077. self.assertEqual(request.META['PATH_INFO'], '/events/')
  1078. self.assertEqual(request.META['SERVER_NAME'], 'localhost')
  1079. self.assertEqual(request.META['SERVER_PORT'], 80)
  1080. self.assertEqual(request.META['SERVER_PROTOCOL'], 'HTTP/1.1')
  1081. self.assertEqual(request.META['wsgi.version'], (1, 0))
  1082. self.assertEqual(request.META['wsgi.url_scheme'], 'http')
  1083. self.assertIn('wsgi.input', request.META)
  1084. self.assertIn('wsgi.errors', request.META)
  1085. self.assertIn('wsgi.multithread', request.META)
  1086. self.assertIn('wsgi.multiprocess', request.META)
  1087. self.assertIn('wsgi.run_once', request.META)
  1088. @override_settings(ALLOWED_HOSTS=['production.example.com'])
  1089. def test_dummy_request_for_inaccessible_page_should_use_valid_host(self):
  1090. root_page = Page.objects.get(url_path='/')
  1091. request = root_page.dummy_request()
  1092. # in the absence of an actual Site record where we can access this page,
  1093. # dummy_request should still provide a hostname that Django's host header
  1094. # validation won't reject
  1095. self.assertEqual(request.META['HTTP_HOST'], 'production.example.com')
  1096. @override_settings(ALLOWED_HOSTS=['*'])
  1097. def test_dummy_request_for_inaccessible_page_with_wildcard_allowed_hosts(self):
  1098. root_page = Page.objects.get(url_path='/')
  1099. request = root_page.dummy_request()
  1100. # '*' is not a valid hostname, so ensure that we replace it with something sensible
  1101. self.assertNotEqual(request.META['HTTP_HOST'], '*')
  1102. class TestShowInMenusDefaultOption(TestCase):
  1103. """
  1104. This tests that a page model can define the default for 'show_in_menus'
  1105. """
  1106. fixtures = ['test.json']
  1107. def test_show_in_menus_default(self):
  1108. # Create a page that does not have the default init
  1109. page = Page(
  1110. title='My Awesome Page', slug='my-awesome-page')
  1111. # Check that the page instance creates with show_in_menu as False
  1112. self.assertFalse(page.show_in_menus)
  1113. def test_show_in_menus_default_override(self):
  1114. # Create a page that does have the default init
  1115. page = AlwaysShowInMenusPage(
  1116. title='My Awesome Page', slug='my-awesome-page')
  1117. # Check that the page instance creates with show_in_menu as True
  1118. self.assertTrue(page.show_in_menus)