2
0

tests.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. import sys
  2. import unittest
  3. from django.conf import settings
  4. from django.contrib.sites.models import Site
  5. from django.contrib.admindocs import utils
  6. from django.contrib.admindocs.views import get_return_data_type
  7. from django.contrib.auth.models import User
  8. from django.core.urlresolvers import reverse
  9. from django.test import TestCase, modify_settings, override_settings
  10. from .models import Person, Company
  11. @override_settings(
  12. PASSWORD_HASHERS=['django.contrib.auth.hashers.SHA1PasswordHasher'],
  13. ROOT_URLCONF='admin_docs.urls')
  14. @modify_settings(INSTALLED_APPS={'append': 'django.contrib.admindocs'})
  15. class AdminDocsTestCase(TestCase):
  16. pass
  17. class MiscTests(AdminDocsTestCase):
  18. def setUp(self):
  19. User.objects.create_superuser('super', None, 'secret')
  20. self.client.login(username='super', password='secret')
  21. @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
  22. @override_settings(SITE_ID=None) # will restore SITE_ID after the test
  23. def test_no_sites_framework(self):
  24. """
  25. Without the sites framework, should not access SITE_ID or Site
  26. objects. Deleting settings is fine here as UserSettingsHolder is used.
  27. """
  28. Site.objects.all().delete()
  29. del settings.SITE_ID
  30. self.client.get('/admindocs/views/') # should not raise
  31. @unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
  32. class AdminDocViewTests(AdminDocsTestCase):
  33. fixtures = ['data.xml']
  34. def setUp(self):
  35. self.client.login(username='super', password='secret')
  36. def test_index(self):
  37. self.client.logout()
  38. response = self.client.get(reverse('django-admindocs-docroot'), follow=True)
  39. # Should display the login screen
  40. self.assertContains(response,
  41. '<input type="hidden" name="next" value="/admindocs/" />', html=True)
  42. self.client.login(username='super', password='secret')
  43. response = self.client.get(reverse('django-admindocs-docroot'))
  44. self.assertContains(response, '<h1>Documentation</h1>', html=True)
  45. self.assertContains(response,
  46. '<h1 id="site-name"><a href="/admin/">Django '
  47. 'administration</a></h1>')
  48. def test_bookmarklets(self):
  49. response = self.client.get(reverse('django-admindocs-bookmarklets'))
  50. self.assertContains(response, 'http://testserver/admin/doc/views/')
  51. def test_templatetag_index(self):
  52. response = self.client.get(reverse('django-admindocs-tags'))
  53. self.assertContains(response, '<h3 id="built_in-extends">extends</h3>', html=True)
  54. def test_templatefilter_index(self):
  55. response = self.client.get(reverse('django-admindocs-filters'))
  56. self.assertContains(response, '<h3 id="built_in-first">first</h3>', html=True)
  57. def test_view_index(self):
  58. response = self.client.get(reverse('django-admindocs-views-index'))
  59. self.assertContains(response,
  60. '<h3><a href="/admindocs/views/django.contrib.admindocs.views.BaseAdminDocsView/">/admindocs/</a></h3>',
  61. html=True)
  62. self.assertContains(response, 'Views by namespace test')
  63. self.assertContains(response, 'Name: <code>test:func</code>.')
  64. def test_view_detail(self):
  65. response = self.client.get(
  66. reverse('django-admindocs-views-detail',
  67. args=['django.contrib.admindocs.views.BaseAdminDocsView']))
  68. # View docstring
  69. self.assertContains(response, 'Base view for admindocs views.')
  70. def test_view_detail_illegal_import(self):
  71. """
  72. #23601 - Ensure the view exists in the URLconf.
  73. """
  74. response = self.client.get(
  75. reverse('django-admindocs-views-detail',
  76. args=['urlpatterns_reverse.nonimported_module.view']))
  77. self.assertEqual(response.status_code, 404)
  78. self.assertNotIn("urlpatterns_reverse.nonimported_module", sys.modules)
  79. def test_model_index(self):
  80. response = self.client.get(reverse('django-admindocs-models-index'))
  81. self.assertContains(
  82. response,
  83. '<h2 id="app-auth">Authentication and Authorization (django.contrib.auth)</h2>',
  84. html=True
  85. )
  86. def test_template_detail(self):
  87. response = self.client.get(reverse('django-admindocs-templates',
  88. args=['admin_doc/template_detail.html']))
  89. self.assertContains(response,
  90. '<h1>Template: "admin_doc/template_detail.html"</h1>', html=True)
  91. def test_missing_docutils(self):
  92. utils.docutils_is_available = False
  93. try:
  94. response = self.client.get(reverse('django-admindocs-docroot'))
  95. self.assertContains(response,
  96. '<h3>The admin documentation system requires Python\'s '
  97. '<a href="http://docutils.sf.net/">docutils</a> library.</h3>',
  98. html=True)
  99. self.assertContains(response,
  100. '<h1 id="site-name"><a href="/admin/">Django '
  101. 'administration</a></h1>')
  102. finally:
  103. utils.docutils_is_available = True
  104. class XViewMiddlewareTest(AdminDocsTestCase):
  105. fixtures = ['data.xml']
  106. def test_xview_func(self):
  107. user = User.objects.get(username='super')
  108. response = self.client.head('/xview/func/')
  109. self.assertNotIn('X-View', response)
  110. self.client.login(username='super', password='secret')
  111. response = self.client.head('/xview/func/')
  112. self.assertIn('X-View', response)
  113. self.assertEqual(response['X-View'], 'admin_docs.views.xview')
  114. user.is_staff = False
  115. user.save()
  116. response = self.client.head('/xview/func/')
  117. self.assertNotIn('X-View', response)
  118. user.is_staff = True
  119. user.is_active = False
  120. user.save()
  121. response = self.client.head('/xview/func/')
  122. self.assertNotIn('X-View', response)
  123. def test_xview_class(self):
  124. user = User.objects.get(username='super')
  125. response = self.client.head('/xview/class/')
  126. self.assertNotIn('X-View', response)
  127. self.client.login(username='super', password='secret')
  128. response = self.client.head('/xview/class/')
  129. self.assertIn('X-View', response)
  130. self.assertEqual(response['X-View'], 'admin_docs.views.XViewClass')
  131. user.is_staff = False
  132. user.save()
  133. response = self.client.head('/xview/class/')
  134. self.assertNotIn('X-View', response)
  135. user.is_staff = True
  136. user.is_active = False
  137. user.save()
  138. response = self.client.head('/xview/class/')
  139. self.assertNotIn('X-View', response)
  140. @unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
  141. class DefaultRoleTest(AdminDocsTestCase):
  142. def test_parse_rst(self):
  143. """
  144. Tests that ``django.contrib.admindocs.utils.parse_rst`` uses
  145. ``cmsreference`` as the default role.
  146. """
  147. markup = ('<p><a class="reference external" href="/admindocs/%s">'
  148. 'title</a></p>\n')
  149. self.assertEqual(utils.parse_rst('`title`', 'model'),
  150. markup % 'models/title/')
  151. self.assertEqual(utils.parse_rst('`title`', 'view'),
  152. markup % 'views/title/')
  153. self.assertEqual(utils.parse_rst('`title`', 'template'),
  154. markup % 'templates/title/')
  155. self.assertEqual(utils.parse_rst('`title`', 'filter'),
  156. markup % 'filters/#title')
  157. self.assertEqual(utils.parse_rst('`title`', 'tag'),
  158. markup % 'tags/#title')
  159. def test_publish_parts(self):
  160. """
  161. Tests that Django hasn't broken the default role for interpreted text
  162. when ``publish_parts`` is used directly, by setting it to
  163. ``cmsreference``. See #6681.
  164. """
  165. import docutils
  166. self.assertNotEqual(docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE,
  167. 'cmsreference')
  168. source = 'reST, `interpreted text`, default role.'
  169. markup = '<p>reST, <cite>interpreted text</cite>, default role.</p>\n'
  170. parts = docutils.core.publish_parts(source=source, writer_name="html4css1")
  171. self.assertEqual(parts['fragment'], markup)
  172. @unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
  173. class TestModelDetailView(AdminDocsTestCase):
  174. """
  175. Tests that various details render correctly
  176. """
  177. fixtures = ['data.xml']
  178. def setUp(self):
  179. self.client.login(username='super', password='secret')
  180. self.response = self.client.get(
  181. reverse('django-admindocs-models-detail',
  182. args=['admin_docs', 'person']))
  183. def test_method_excludes(self):
  184. """
  185. Test that methods that begin with strings defined in
  186. ``django.contrib.admindocs.views.MODEL_METHODS_EXCLUDE``
  187. do not get displayed in the admin docs
  188. """
  189. self.assertContains(self.response, "<td>get_full_name</td>")
  190. self.assertNotContains(self.response, "<td>_get_full_name</td>")
  191. self.assertNotContains(self.response, "<td>add_image</td>")
  192. self.assertNotContains(self.response, "<td>delete_image</td>")
  193. self.assertNotContains(self.response, "<td>set_status</td>")
  194. self.assertNotContains(self.response, "<td>save_changes</td>")
  195. def test_method_data_types(self):
  196. """
  197. We should be able to get a basic idea of the type returned
  198. by a method
  199. """
  200. company = Company.objects.create(name="Django")
  201. person = Person.objects.create(
  202. first_name="Human",
  203. last_name="User",
  204. company=company
  205. )
  206. self.assertEqual(
  207. get_return_data_type(person.get_status_count.__name__),
  208. 'Integer'
  209. )
  210. self.assertEqual(
  211. get_return_data_type(person.get_groups_list.__name__),
  212. 'List'
  213. )
  214. def test_descriptions_render_correctly(self):
  215. """
  216. The ``description`` field should render correctly for each type of field
  217. """
  218. # help text in fields
  219. self.assertContains(self.response, "<td>first name - The person's first name</td>")
  220. self.assertContains(self.response, "<td>last name - The person's last name</td>")
  221. # method docstrings
  222. self.assertContains(self.response, "<p>Get the full name of the person</p>")
  223. link = '<a class="reference external" href="/admindocs/models/%s/">%s</a>'
  224. markup = '<p>the related %s object</p>'
  225. company_markup = markup % (link % ("admin_docs.company", "admin_docs.Company"))
  226. # foreign keys
  227. self.assertContains(self.response, company_markup)
  228. # foreign keys with help text
  229. self.assertContains(self.response, "%s\n - place of work" % company_markup)
  230. # many to many fields
  231. self.assertContains(
  232. self.response,
  233. "number of related %s objects" % (link % ("admin_docs.group", "admin_docs.Group"))
  234. )
  235. self.assertContains(
  236. self.response,
  237. "all related %s objects" % (link % ("admin_docs.group", "admin_docs.Group"))
  238. )
  239. def test_model_with_no_backward_relations_render_only_relevant_fields(self):
  240. """
  241. A model with ``related_name`` of `+` should not show backward relationship
  242. links in admin docs
  243. """
  244. response = self.client.get(
  245. reverse('django-admindocs-models-detail',
  246. args=['admin_docs', 'family']))
  247. fields = response.context_data.get('fields')
  248. self.assertEqual(len(fields), 2)
  249. def test_model_docstring_renders_correctly(self):
  250. summary = (
  251. '<h2 class="subhead"><p>Stores information about a person, related to <a class="reference external" '
  252. 'href="/admindocs/models/myapp.company/">myapp.Company</a>.</p></h2>'
  253. )
  254. subheading = '<p><strong>Notes</strong></p>'
  255. body = '<p>Use <tt class="docutils literal">save_changes()</tt> when saving this object.</p>'
  256. model_body = (
  257. '<dl class="docutils"><dt><tt class="'
  258. 'docutils literal">company</tt></dt><dd>Field storing <a class="'
  259. 'reference external" href="/admindocs/models/myapp.company/">'
  260. 'myapp.Company</a> where the person works.</dd></dl>'
  261. )
  262. self.assertContains(self.response, 'DESCRIPTION')
  263. self.assertContains(self.response, summary, html=True)
  264. self.assertContains(self.response, subheading, html=True)
  265. self.assertContains(self.response, body, html=True)
  266. self.assertContains(self.response, model_body, html=True)
  267. @unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
  268. class TestUtils(AdminDocsTestCase):
  269. """
  270. This __doc__ output is required for testing. I copied this example from
  271. `admindocs` documentation. (TITLE)
  272. Display an individual :model:`myapp.MyModel`.
  273. **Context**
  274. ``RequestContext``
  275. ``mymodel``
  276. An instance of :model:`myapp.MyModel`.
  277. **Template:**
  278. :template:`myapp/my_template.html` (DESCRIPTION)
  279. some_metadata: some data
  280. """
  281. def setUp(self):
  282. self.docstring = self.__doc__
  283. def test_trim_docstring(self):
  284. trim_docstring_output = utils.trim_docstring(self.docstring)
  285. trimmed_docstring = (
  286. 'This __doc__ output is required for testing. I copied this '
  287. 'example from\n`admindocs` documentation. (TITLE)\n\n'
  288. 'Display an individual :model:`myapp.MyModel`.\n\n'
  289. '**Context**\n\n``RequestContext``\n\n``mymodel``\n'
  290. ' An instance of :model:`myapp.MyModel`.\n\n'
  291. '**Template:**\n\n:template:`myapp/my_template.html` '
  292. '(DESCRIPTION)\n\nsome_metadata: some data'
  293. )
  294. self.assertEqual(trim_docstring_output, trimmed_docstring)
  295. def test_parse_docstring(self):
  296. title, description, metadata = utils.parse_docstring(self.docstring)
  297. docstring_title = (
  298. 'This __doc__ output is required for testing. I copied this example from\n'
  299. '`admindocs` documentation. (TITLE)'
  300. )
  301. docstring_description = (
  302. 'Display an individual :model:`myapp.MyModel`.\n\n'
  303. '**Context**\n\n``RequestContext``\n\n``mymodel``\n'
  304. ' An instance of :model:`myapp.MyModel`.\n\n'
  305. '**Template:**\n\n:template:`myapp/my_template.html` '
  306. '(DESCRIPTION)'
  307. )
  308. self.assertEqual(title, docstring_title)
  309. self.assertEqual(description, docstring_description)
  310. self.assertEqual(metadata, {'some_metadata': 'some data'})
  311. def test_title_output(self):
  312. title, description, metadata = utils.parse_docstring(self.docstring)
  313. title_output = utils.parse_rst(title, 'model', 'model:admindocs')
  314. self.assertIn('TITLE', title_output)
  315. title_rendered = (
  316. '<p>This __doc__ output is required for testing. I copied this '
  317. 'example from\n<a class="reference external" '
  318. 'href="/admindocs/models/admindocs/">admindocs</a> documentation. '
  319. '(TITLE)</p>\n'
  320. )
  321. self.assertHTMLEqual(title_output, title_rendered)
  322. def test_description_output(self):
  323. title, description, metadata = utils.parse_docstring(self.docstring)
  324. description_output = utils.parse_rst(description, 'model', 'model:admindocs')
  325. description_rendered = (
  326. '<p>Display an individual <a class="reference external" '
  327. 'href="/admindocs/models/myapp.mymodel/">myapp.MyModel</a>.</p>\n'
  328. '<p><strong>Context</strong></p>\n<p><tt class="docutils literal">'
  329. 'RequestContext</tt></p>\n<dl class="docutils">\n<dt><tt class="'
  330. 'docutils literal">mymodel</tt></dt>\n<dd>An instance of <a class="'
  331. 'reference external" href="/admindocs/models/myapp.mymodel/">'
  332. 'myapp.MyModel</a>.</dd>\n</dl>\n<p><strong>Template:</strong></p>'
  333. '\n<p><a class="reference external" href="/admindocs/templates/'
  334. 'myapp/my_template.html/">myapp/my_template.html</a> (DESCRIPTION)'
  335. '</p>\n'
  336. )
  337. self.assertHTMLEqual(description_output, description_rendered)