diff options
| author | Tim Graham <timograham@gmail.com> | 2016-09-27 15:37:32 -0400 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2016-09-27 18:54:35 -0400 |
| commit | 231743414710c60090728e86f33d81cbc43df1d1 (patch) | |
| tree | 20e0a10acbbc3d98a6d8c8cbe7b544d3fbac6a2d /tests/admin_docs | |
| parent | 62543260dd58309902fa73f0984e245fd6c6da2a (diff) | |
Split admin_docs tests into separate files.
Diffstat (limited to 'tests/admin_docs')
| -rw-r--r-- | tests/admin_docs/test_middleware.py | 44 | ||||
| -rw-r--r-- | tests/admin_docs/test_utils.py | 122 | ||||
| -rw-r--r-- | tests/admin_docs/test_views.py | 340 | ||||
| -rw-r--r-- | tests/admin_docs/tests.py | 477 |
4 files changed, 507 insertions, 476 deletions
diff --git a/tests/admin_docs/test_middleware.py b/tests/admin_docs/test_middleware.py new file mode 100644 index 0000000000..426c78d58f --- /dev/null +++ b/tests/admin_docs/test_middleware.py @@ -0,0 +1,44 @@ +from __future__ import unicode_literals + +from django.contrib.auth.models import User + +from .tests import AdminDocsTestCase, TestDataMixin + + +class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase): + + def test_xview_func(self): + user = User.objects.get(username='super') + response = self.client.head('/xview/func/') + self.assertNotIn('X-View', response) + self.client.force_login(self.superuser) + response = self.client.head('/xview/func/') + self.assertIn('X-View', response) + self.assertEqual(response['X-View'], 'admin_docs.views.xview') + user.is_staff = False + user.save() + response = self.client.head('/xview/func/') + self.assertNotIn('X-View', response) + user.is_staff = True + user.is_active = False + user.save() + response = self.client.head('/xview/func/') + self.assertNotIn('X-View', response) + + def test_xview_class(self): + user = User.objects.get(username='super') + response = self.client.head('/xview/class/') + self.assertNotIn('X-View', response) + self.client.force_login(self.superuser) + response = self.client.head('/xview/class/') + self.assertIn('X-View', response) + self.assertEqual(response['X-View'], 'admin_docs.views.XViewClass') + user.is_staff = False + user.save() + response = self.client.head('/xview/class/') + self.assertNotIn('X-View', response) + user.is_staff = True + user.is_active = False + user.save() + response = self.client.head('/xview/class/') + self.assertNotIn('X-View', response) diff --git a/tests/admin_docs/test_utils.py b/tests/admin_docs/test_utils.py new file mode 100644 index 0000000000..013d003914 --- /dev/null +++ b/tests/admin_docs/test_utils.py @@ -0,0 +1,122 @@ +from __future__ import unicode_literals + +import unittest + +from django.contrib.admindocs.utils import ( + docutils_is_available, parse_docstring, parse_rst, trim_docstring, +) + +from .tests import AdminDocsTestCase + + +@unittest.skipUnless(docutils_is_available, "no docutils installed.") +class TestUtils(AdminDocsTestCase): + """ + This __doc__ output is required for testing. I copied this example from + `admindocs` documentation. (TITLE) + + Display an individual :model:`myapp.MyModel`. + + **Context** + + ``RequestContext`` + + ``mymodel`` + An instance of :model:`myapp.MyModel`. + + **Template:** + + :template:`myapp/my_template.html` (DESCRIPTION) + + some_metadata: some data + """ + def setUp(self): + self.docstring = self.__doc__ + + def test_trim_docstring(self): + trim_docstring_output = trim_docstring(self.docstring) + trimmed_docstring = ( + 'This __doc__ output is required for testing. I copied this ' + 'example from\n`admindocs` documentation. (TITLE)\n\n' + 'Display an individual :model:`myapp.MyModel`.\n\n' + '**Context**\n\n``RequestContext``\n\n``mymodel``\n' + ' An instance of :model:`myapp.MyModel`.\n\n' + '**Template:**\n\n:template:`myapp/my_template.html` ' + '(DESCRIPTION)\n\nsome_metadata: some data' + ) + self.assertEqual(trim_docstring_output, trimmed_docstring) + + def test_parse_docstring(self): + title, description, metadata = parse_docstring(self.docstring) + docstring_title = ( + 'This __doc__ output is required for testing. I copied this example from\n' + '`admindocs` documentation. (TITLE)' + ) + docstring_description = ( + 'Display an individual :model:`myapp.MyModel`.\n\n' + '**Context**\n\n``RequestContext``\n\n``mymodel``\n' + ' An instance of :model:`myapp.MyModel`.\n\n' + '**Template:**\n\n:template:`myapp/my_template.html` ' + '(DESCRIPTION)' + ) + self.assertEqual(title, docstring_title) + self.assertEqual(description, docstring_description) + self.assertEqual(metadata, {'some_metadata': 'some data'}) + + def test_title_output(self): + title, description, metadata = parse_docstring(self.docstring) + title_output = parse_rst(title, 'model', 'model:admindocs') + self.assertIn('TITLE', title_output) + title_rendered = ( + '<p>This __doc__ output is required for testing. I copied this ' + 'example from\n<a class="reference external" ' + 'href="/admindocs/models/admindocs/">admindocs</a> documentation. ' + '(TITLE)</p>\n' + ) + self.assertHTMLEqual(title_output, title_rendered) + + def test_description_output(self): + title, description, metadata = parse_docstring(self.docstring) + description_output = parse_rst(description, 'model', 'model:admindocs') + description_rendered = ( + '<p>Display an individual <a class="reference external" ' + 'href="/admindocs/models/myapp.mymodel/">myapp.MyModel</a>.</p>\n' + '<p><strong>Context</strong></p>\n<p><tt class="docutils literal">' + 'RequestContext</tt></p>\n<dl class="docutils">\n<dt><tt class="' + 'docutils literal">mymodel</tt></dt>\n<dd>An instance of <a class="' + 'reference external" href="/admindocs/models/myapp.mymodel/">' + 'myapp.MyModel</a>.</dd>\n</dl>\n<p><strong>Template:</strong></p>' + '\n<p><a class="reference external" href="/admindocs/templates/' + 'myapp/my_template.html/">myapp/my_template.html</a> (DESCRIPTION)' + '</p>\n' + ) + self.assertHTMLEqual(description_output, description_rendered) + + def test_initial_header_level(self): + header = 'should be h3...\n\nHeader\n------\n' + output = parse_rst(header, 'header') + self.assertIn('<h3>Header</h3>', output) + + def test_parse_rst(self): + """ + parse_rst() should use `cmsreference` as the default role. + """ + markup = '<p><a class="reference external" href="/admindocs/%s">title</a></p>\n' + self.assertEqual(parse_rst('`title`', 'model'), markup % 'models/title/') + self.assertEqual(parse_rst('`title`', 'view'), markup % 'views/title/') + self.assertEqual(parse_rst('`title`', 'template'), markup % 'templates/title/') + self.assertEqual(parse_rst('`title`', 'filter'), markup % 'filters/#title') + self.assertEqual(parse_rst('`title`', 'tag'), markup % 'tags/#title') + + def test_publish_parts(self): + """ + Django shouldn't break the default role for interpreted text + when ``publish_parts`` is used directly, by setting it to + ``cmsreference`` (#6681). + """ + import docutils + self.assertNotEqual(docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE, 'cmsreference') + source = 'reST, `interpreted text`, default role.' + markup = '<p>reST, <cite>interpreted text</cite>, default role.</p>\n' + parts = docutils.core.publish_parts(source=source, writer_name="html4css1") + self.assertEqual(parts['fragment'], markup) diff --git a/tests/admin_docs/test_views.py b/tests/admin_docs/test_views.py new file mode 100644 index 0000000000..a5134d7627 --- /dev/null +++ b/tests/admin_docs/test_views.py @@ -0,0 +1,340 @@ +from __future__ import unicode_literals + +import sys +import unittest + +from django.conf import settings +from django.contrib.admindocs import utils, views +from django.contrib.admindocs.views import get_return_data_type, simplify_regex +from django.contrib.sites.models import Site +from django.db import models +from django.db.models import fields +from django.test import SimpleTestCase, modify_settings, override_settings +from django.test.utils import captured_stderr +from django.urls import reverse +from django.utils import six + +from .models import Company, Person +from .tests import AdminDocsTestCase, TestDataMixin + + +@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") +class AdminDocViewTests(TestDataMixin, AdminDocsTestCase): + + def setUp(self): + self.client.force_login(self.superuser) + + def test_index(self): + response = self.client.get(reverse('django-admindocs-docroot')) + self.assertContains(response, '<h1>Documentation</h1>', html=True) + self.assertContains(response, '<h1 id="site-name"><a href="/admin/">Django administration</a></h1>') + self.client.logout() + response = self.client.get(reverse('django-admindocs-docroot'), follow=True) + # Should display the login screen + self.assertContains(response, '<input type="hidden" name="next" value="/admindocs/" />', html=True) + + def test_bookmarklets(self): + response = self.client.get(reverse('django-admindocs-bookmarklets')) + self.assertContains(response, '/admindocs/views/') + + def test_templatetag_index(self): + response = self.client.get(reverse('django-admindocs-tags')) + self.assertContains(response, '<h3 id="built_in-extends">extends</h3>', html=True) + + def test_templatefilter_index(self): + response = self.client.get(reverse('django-admindocs-filters')) + self.assertContains(response, '<h3 id="built_in-first">first</h3>', html=True) + + def test_view_index(self): + response = self.client.get(reverse('django-admindocs-views-index')) + self.assertContains( + response, + '<h3><a href="/admindocs/views/django.contrib.admindocs.views.BaseAdminDocsView/">/admindocs/</a></h3>', + html=True + ) + self.assertContains(response, 'Views by namespace test') + self.assertContains(response, 'Name: <code>test:func</code>.') + + @unittest.skipIf(six.PY2, "Python 2 doesn't support __qualname__.") + def test_view_index_with_method(self): + """ + Views that are methods are listed correctly. + """ + response = self.client.get(reverse('django-admindocs-views-index')) + self.assertContains( + response, + '<h3><a href="/admindocs/views/django.contrib.admin.sites.AdminSite.index/">/admin/</a></h3>', + html=True + ) + + def test_view_detail(self): + url = reverse('django-admindocs-views-detail', args=['django.contrib.admindocs.views.BaseAdminDocsView']) + response = self.client.get(url) + # View docstring + self.assertContains(response, 'Base view for admindocs views.') + + @override_settings(ROOT_URLCONF='admin_docs.namespace_urls') + def test_namespaced_view_detail(self): + url = reverse('django-admindocs-views-detail', args=['admin_docs.views.XViewClass']) + response = self.client.get(url) + self.assertContains(response, '<h1>admin_docs.views.XViewClass</h1>') + + def test_view_detail_illegal_import(self): + url = reverse('django-admindocs-views-detail', args=['urlpatterns_reverse.nonimported_module.view']) + response = self.client.get(url) + self.assertEqual(response.status_code, 404) + self.assertNotIn("urlpatterns_reverse.nonimported_module", sys.modules) + + def test_view_detail_as_method(self): + """ + Views that are methods can be displayed. + """ + url = reverse('django-admindocs-views-detail', args=['django.contrib.admin.sites.AdminSite.index']) + response = self.client.get(url) + self.assertEqual(response.status_code, 200 if six.PY3 else 404) + + def test_model_index(self): + response = self.client.get(reverse('django-admindocs-models-index')) + self.assertContains( + response, + '<h2 id="app-auth">Authentication and Authorization (django.contrib.auth)</h2>', + html=True + ) + + def test_template_detail(self): + response = self.client.get(reverse('django-admindocs-templates', args=['admin_doc/template_detail.html'])) + self.assertContains(response, '<h1>Template: "admin_doc/template_detail.html"</h1>', html=True) + + def test_missing_docutils(self): + utils.docutils_is_available = False + try: + response = self.client.get(reverse('django-admindocs-docroot')) + self.assertContains( + response, + '<h3>The admin documentation system requires Python\'s ' + '<a href="http://docutils.sf.net/">docutils</a> library.</h3>', + html=True + ) + self.assertContains(response, '<h1 id="site-name"><a href="/admin/">Django administration</a></h1>') + finally: + utils.docutils_is_available = True + + @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}) + @override_settings(SITE_ID=None) # will restore SITE_ID after the test + def test_no_sites_framework(self): + """ + Without the sites framework, should not access SITE_ID or Site + objects. Deleting settings is fine here as UserSettingsHolder is used. + """ + Site.objects.all().delete() + del settings.SITE_ID + response = self.client.get(reverse('django-admindocs-views-index')) + self.assertContains(response, 'View documentation') + + +@override_settings(TEMPLATES=[{ + 'NAME': 'ONE', + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, +}, { + 'NAME': 'TWO', + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, +}]) +@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") +class AdminDocViewWithMultipleEngines(AdminDocViewTests): + + def test_templatefilter_index(self): + # Overridden because non-trivial TEMPLATES settings aren't supported + # but the page shouldn't crash (#24125). + response = self.client.get(reverse('django-admindocs-filters')) + self.assertContains(response, '<title>Template filters</title>', html=True) + + def test_templatetag_index(self): + # Overridden because non-trivial TEMPLATES settings aren't supported + # but the page shouldn't crash (#24125). + response = self.client.get(reverse('django-admindocs-tags')) + self.assertContains(response, '<title>Template tags</title>', html=True) + + +@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") +class TestModelDetailView(TestDataMixin, AdminDocsTestCase): + + def setUp(self): + self.client.force_login(self.superuser) + with captured_stderr() as self.docutils_stderr: + self.response = self.client.get(reverse('django-admindocs-models-detail', args=['admin_docs', 'Person'])) + + def test_method_excludes(self): + """ + Methods that begin with strings defined in + ``django.contrib.admindocs.views.MODEL_METHODS_EXCLUDE`` + shouldn't be displayed in the admin docs. + """ + self.assertContains(self.response, "<td>get_full_name</td>") + self.assertNotContains(self.response, "<td>_get_full_name</td>") + self.assertNotContains(self.response, "<td>add_image</td>") + self.assertNotContains(self.response, "<td>delete_image</td>") + self.assertNotContains(self.response, "<td>set_status</td>") + self.assertNotContains(self.response, "<td>save_changes</td>") + + def test_methods_with_arguments(self): + """ + Methods that take arguments should also displayed. + """ + self.assertContains(self.response, "<h3>Methods with arguments</h3>") + self.assertContains(self.response, "<td>rename_company</td>") + self.assertContains(self.response, "<td>dummy_function</td>") + self.assertContains(self.response, "<td>suffix_company_name</td>") + + def test_methods_with_arguments_display_arguments(self): + """ + Methods with arguments should have their arguments displayed. + """ + self.assertContains(self.response, "<td>new_name</td>") + + def test_methods_with_arguments_display_arguments_default_value(self): + """ + Methods with keyword arguments should have their arguments displayed. + """ + self.assertContains(self.response, "<td>suffix='ltd'</td>") + + def test_methods_with_multiple_arguments_display_arguments(self): + """ + Methods with multiple arguments should have all their arguments + displayed, but omitting 'self'. + """ + self.assertContains(self.response, "<td>baz, rox, *some_args, **some_kwargs</td>") + + def test_method_data_types(self): + company = Company.objects.create(name="Django") + person = Person.objects.create(first_name="Human", last_name="User", company=company) + self.assertEqual(get_return_data_type(person.get_status_count.__name__), 'Integer') + self.assertEqual(get_return_data_type(person.get_groups_list.__name__), 'List') + + def test_descriptions_render_correctly(self): + """ + The ``description`` field should render correctly for each field type. + """ + # help text in fields + self.assertContains(self.response, "<td>first name - The person's first name</td>") + self.assertContains(self.response, "<td>last name - The person's last name</td>") + + # method docstrings + self.assertContains(self.response, "<p>Get the full name of the person</p>") + + link = '<a class="reference external" href="/admindocs/models/%s/">%s</a>' + markup = '<p>the related %s object</p>' + company_markup = markup % (link % ("admin_docs.company", "admin_docs.Company")) + + # foreign keys + self.assertContains(self.response, company_markup) + + # foreign keys with help text + self.assertContains(self.response, "%s\n - place of work" % company_markup) + + # many to many fields + self.assertContains( + self.response, + "number of related %s objects" % (link % ("admin_docs.group", "admin_docs.Group")) + ) + self.assertContains( + self.response, + "all related %s objects" % (link % ("admin_docs.group", "admin_docs.Group")) + ) + + # "raw" and "include" directives are disabled + self.assertContains(self.response, '<p>"raw" directive disabled.</p>',) + self.assertContains(self.response, '.. raw:: html\n :file: admin_docs/evilfile.txt') + self.assertContains(self.response, '<p>"include" directive disabled.</p>',) + self.assertContains(self.response, '.. include:: admin_docs/evilfile.txt') + out = self.docutils_stderr.getvalue() + self.assertIn('"raw" directive disabled', out) + self.assertIn('"include" directive disabled', out) + + def test_model_with_many_to_one(self): + link = '<a class="reference external" href="/admindocs/models/%s/">%s</a>' + response = self.client.get( + reverse('django-admindocs-models-detail', args=['admin_docs', 'company']) + ) + self.assertContains( + response, + "number of related %s objects" % (link % ("admin_docs.person", "admin_docs.Person")) + ) + self.assertContains( + response, + "all related %s objects" % (link % ("admin_docs.person", "admin_docs.Person")) + ) + + def test_model_with_no_backward_relations_render_only_relevant_fields(self): + """ + A model with ``related_name`` of `+` shouldn't show backward + relationship links. + """ + response = self.client.get(reverse('django-admindocs-models-detail', args=['admin_docs', 'family'])) + fields = response.context_data.get('fields') + self.assertEqual(len(fields), 2) + + def test_model_docstring_renders_correctly(self): + summary = ( + '<h2 class="subhead"><p>Stores information about a person, related to <a class="reference external" ' + 'href="/admindocs/models/myapp.company/">myapp.Company</a>.</p></h2>' + ) + subheading = '<p><strong>Notes</strong></p>' + body = '<p>Use <tt class="docutils literal">save_changes()</tt> when saving this object.</p>' + model_body = ( + '<dl class="docutils"><dt><tt class="' + 'docutils literal">company</tt></dt><dd>Field storing <a class="' + 'reference external" href="/admindocs/models/myapp.company/">' + 'myapp.Company</a> where the person works.</dd></dl>' + ) + self.assertContains(self.response, 'DESCRIPTION') + self.assertContains(self.response, summary, html=True) + self.assertContains(self.response, subheading, html=True) + self.assertContains(self.response, body, html=True) + self.assertContains(self.response, model_body, html=True) + + def test_model_detail_title(self): + self.assertContains(self.response, '<h1>admin_docs.Person</h1>', html=True) + + +class CustomField(models.Field): + description = "A custom field type" + + +class DescriptionLackingField(models.Field): + pass + + +class TestFieldType(unittest.TestCase): + def setUp(self): + pass + + def test_field_name(self): + with self.assertRaises(AttributeError): + views.get_readable_field_data_type("NotAField") + + def test_builtin_fields(self): + self.assertEqual( + views.get_readable_field_data_type(fields.BooleanField()), + 'Boolean (Either True or False)' + ) + + def test_custom_fields(self): + self.assertEqual(views.get_readable_field_data_type(CustomField()), 'A custom field type') + self.assertEqual( + views.get_readable_field_data_type(DescriptionLackingField()), + 'Field of type: DescriptionLackingField' + ) + + +class AdminDocViewFunctionsTests(SimpleTestCase): + + def test_simplify_regex(self): + tests = ( + (r'^a', '/a'), + (r'^(?P<a>\w+)/b/(?P<c>\w+)/$', '/<a>/b/<c>/'), + (r'^(?P<a>\w+)/b/(?P<c>\w+)$', '/<a>/b/<c>'), + ) + for pattern, output in tests: + self.assertEqual(simplify_regex(pattern), output) diff --git a/tests/admin_docs/tests.py b/tests/admin_docs/tests.py index 6f6f8838f3..c376a4cf5e 100644 --- a/tests/admin_docs/tests.py +++ b/tests/admin_docs/tests.py @@ -1,17 +1,7 @@ -import sys -import unittest +from __future__ import unicode_literals -from django.conf import settings -from django.contrib.admindocs import utils -from django.contrib.admindocs.views import get_return_data_type, simplify_regex from django.contrib.auth.models import User -from django.contrib.sites.models import Site from django.test import TestCase, modify_settings, override_settings -from django.test.utils import captured_stderr -from django.urls import reverse -from django.utils import six - -from .models import Company, Person class TestDataMixin(object): @@ -25,468 +15,3 @@ class TestDataMixin(object): @modify_settings(INSTALLED_APPS={'append': 'django.contrib.admindocs'}) class AdminDocsTestCase(TestCase): pass - - -class MiscTests(AdminDocsTestCase): - - def setUp(self): - superuser = User.objects.create_superuser('super', None, 'secret') - self.client.force_login(superuser) - - @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}) - @override_settings(SITE_ID=None) # will restore SITE_ID after the test - def test_no_sites_framework(self): - """ - Without the sites framework, should not access SITE_ID or Site - objects. Deleting settings is fine here as UserSettingsHolder is used. - """ - Site.objects.all().delete() - del settings.SITE_ID - self.client.get('/admindocs/views/') # should not raise - - -@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") -class AdminDocViewTests(TestDataMixin, AdminDocsTestCase): - - def setUp(self): - self.client.force_login(self.superuser) - - def test_index(self): - self.client.logout() - response = self.client.get(reverse('django-admindocs-docroot'), follow=True) - # Should display the login screen - self.assertContains(response, '<input type="hidden" name="next" value="/admindocs/" />', html=True) - self.client.force_login(self.superuser) - response = self.client.get(reverse('django-admindocs-docroot')) - self.assertContains(response, '<h1>Documentation</h1>', html=True) - self.assertContains(response, '<h1 id="site-name"><a href="/admin/">Django administration</a></h1>') - - def test_bookmarklets(self): - response = self.client.get(reverse('django-admindocs-bookmarklets')) - self.assertContains(response, '/admindocs/views/') - - def test_templatetag_index(self): - response = self.client.get(reverse('django-admindocs-tags')) - self.assertContains(response, '<h3 id="built_in-extends">extends</h3>', html=True) - - def test_templatefilter_index(self): - response = self.client.get(reverse('django-admindocs-filters')) - self.assertContains(response, '<h3 id="built_in-first">first</h3>', html=True) - - def test_view_index(self): - response = self.client.get(reverse('django-admindocs-views-index')) - self.assertContains( - response, - '<h3><a href="/admindocs/views/django.contrib.admindocs.views.BaseAdminDocsView/">/admindocs/</a></h3>', - html=True - ) - self.assertContains(response, 'Views by namespace test') - self.assertContains(response, 'Name: <code>test:func</code>.') - - @unittest.skipIf(six.PY2, "Python 2 doesn't support __qualname__.") - def test_view_index_with_method(self): - """ - Views that are methods are listed correctly. - """ - response = self.client.get(reverse('django-admindocs-views-index')) - self.assertContains( - response, - '<h3><a href="/admindocs/views/django.contrib.admin.sites.AdminSite.index/">/admin/</a></h3>', - html=True - ) - - def test_view_detail(self): - url = reverse('django-admindocs-views-detail', args=['django.contrib.admindocs.views.BaseAdminDocsView']) - response = self.client.get(url) - # View docstring - self.assertContains(response, 'Base view for admindocs views.') - - @override_settings(ROOT_URLCONF='admin_docs.namespace_urls') - def test_namespaced_view_detail(self): - url = reverse('django-admindocs-views-detail', args=['admin_docs.views.XViewClass']) - response = self.client.get(url) - self.assertContains(response, '<h1>admin_docs.views.XViewClass</h1>') - - def test_view_detail_illegal_import(self): - """ - #23601 - Ensure the view exists in the URLconf. - """ - url = reverse('django-admindocs-views-detail', args=['urlpatterns_reverse.nonimported_module.view']) - response = self.client.get(url) - self.assertEqual(response.status_code, 404) - self.assertNotIn("urlpatterns_reverse.nonimported_module", sys.modules) - - def test_view_detail_as_method(self): - """ - Views that are methods can be displayed. - """ - url = reverse('django-admindocs-views-detail', args=['django.contrib.admin.sites.AdminSite.index']) - response = self.client.get(url) - self.assertEqual(response.status_code, 200 if six.PY3 else 404) - - def test_model_index(self): - response = self.client.get(reverse('django-admindocs-models-index')) - self.assertContains( - response, - '<h2 id="app-auth">Authentication and Authorization (django.contrib.auth)</h2>', - html=True - ) - - def test_template_detail(self): - response = self.client.get(reverse('django-admindocs-templates', args=['admin_doc/template_detail.html'])) - self.assertContains(response, '<h1>Template: "admin_doc/template_detail.html"</h1>', html=True) - - def test_missing_docutils(self): - utils.docutils_is_available = False - try: - response = self.client.get(reverse('django-admindocs-docroot')) - self.assertContains( - response, - '<h3>The admin documentation system requires Python\'s ' - '<a href="http://docutils.sf.net/">docutils</a> library.</h3>', - html=True - ) - self.assertContains(response, '<h1 id="site-name"><a href="/admin/">Django administration</a></h1>') - finally: - utils.docutils_is_available = True - - def test_simplify_regex(self): - tests = ( - ('^a', '/a'), - (r'^(?P<a>\w+)/b/(?P<c>\w+)/$', '/<a>/b/<c>/'), - (r'^(?P<a>\w+)/b/(?P<c>\w+)$', '/<a>/b/<c>'), - ) - for pattern, output in tests: - self.assertEqual(simplify_regex(pattern), output) - - -@override_settings(TEMPLATES=[{ - 'NAME': 'ONE', - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, -}, { - 'NAME': 'TWO', - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, -}]) -@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") -class AdminDocViewWithMultipleEngines(AdminDocViewTests): - def test_templatefilter_index(self): - # Overridden because non-trivial TEMPLATES settings aren't supported - # but the page shouldn't crash (#24125). - response = self.client.get(reverse('django-admindocs-filters')) - self.assertContains(response, '<title>Template filters</title>', html=True) - - def test_templatetag_index(self): - # Overridden because non-trivial TEMPLATES settings aren't supported - # but the page shouldn't crash (#24125). - response = self.client.get(reverse('django-admindocs-tags')) - self.assertContains(response, '<title>Template tags</title>', html=True) - - -class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase): - - def test_xview_func(self): - user = User.objects.get(username='super') - response = self.client.head('/xview/func/') - self.assertNotIn('X-View', response) - self.client.force_login(self.superuser) - response = self.client.head('/xview/func/') - self.assertIn('X-View', response) - self.assertEqual(response['X-View'], 'admin_docs.views.xview') - user.is_staff = False - user.save() - response = self.client.head('/xview/func/') - self.assertNotIn('X-View', response) - user.is_staff = True - user.is_active = False - user.save() - response = self.client.head('/xview/func/') - self.assertNotIn('X-View', response) - - def test_xview_class(self): - user = User.objects.get(username='super') - response = self.client.head('/xview/class/') - self.assertNotIn('X-View', response) - self.client.force_login(self.superuser) - response = self.client.head('/xview/class/') - self.assertIn('X-View', response) - self.assertEqual(response['X-View'], 'admin_docs.views.XViewClass') - user.is_staff = False - user.save() - response = self.client.head('/xview/class/') - self.assertNotIn('X-View', response) - user.is_staff = True - user.is_active = False - user.save() - response = self.client.head('/xview/class/') - self.assertNotIn('X-View', response) - - -@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") -class DefaultRoleTest(AdminDocsTestCase): - - def test_parse_rst(self): - """ - ``django.contrib.admindocs.utils.parse_rst`` should use - ``cmsreference`` as the default role. - """ - markup = '<p><a class="reference external" href="/admindocs/%s">title</a></p>\n' - self.assertEqual(utils.parse_rst('`title`', 'model'), markup % 'models/title/') - self.assertEqual(utils.parse_rst('`title`', 'view'), markup % 'views/title/') - self.assertEqual(utils.parse_rst('`title`', 'template'), markup % 'templates/title/') - self.assertEqual(utils.parse_rst('`title`', 'filter'), markup % 'filters/#title') - self.assertEqual(utils.parse_rst('`title`', 'tag'), markup % 'tags/#title') - - def test_publish_parts(self): - """ - Django shouldn't break the default role for interpreted text - when ``publish_parts`` is used directly, by setting it to - ``cmsreference``. See #6681. - """ - import docutils - self.assertNotEqual(docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE, 'cmsreference') - source = 'reST, `interpreted text`, default role.' - markup = '<p>reST, <cite>interpreted text</cite>, default role.</p>\n' - parts = docutils.core.publish_parts(source=source, writer_name="html4css1") - self.assertEqual(parts['fragment'], markup) - - -@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") -class TestModelDetailView(TestDataMixin, AdminDocsTestCase): - """ - Tests that various details render correctly - """ - - def setUp(self): - self.client.force_login(self.superuser) - with captured_stderr() as self.docutils_stderr: - self.response = self.client.get(reverse('django-admindocs-models-detail', args=['admin_docs', 'Person'])) - - def test_method_excludes(self): - """ - Methods that begin with strings defined in - ``django.contrib.admindocs.views.MODEL_METHODS_EXCLUDE`` - should not get displayed in the admin docs. - """ - self.assertContains(self.response, "<td>get_full_name</td>") - self.assertNotContains(self.response, "<td>_get_full_name</td>") - self.assertNotContains(self.response, "<td>add_image</td>") - self.assertNotContains(self.response, "<td>delete_image</td>") - self.assertNotContains(self.response, "<td>set_status</td>") - self.assertNotContains(self.response, "<td>save_changes</td>") - - def test_methods_with_arguments(self): - """ - Methods that take arguments should also displayed. - """ - self.assertContains(self.response, "<h3>Methods with arguments</h3>") - self.assertContains(self.response, "<td>rename_company</td>") - self.assertContains(self.response, "<td>dummy_function</td>") - self.assertContains(self.response, "<td>suffix_company_name</td>") - - def test_methods_with_arguments_display_arguments(self): - """ - Methods with arguments should have their arguments displayed. - """ - self.assertContains(self.response, "<td>new_name</td>") - - def test_methods_with_arguments_display_arguments_default_value(self): - """ - Methods with keyword arguments should have their arguments displayed. - """ - self.assertContains(self.response, "<td>suffix='ltd'</td>") - - def test_methods_with_multiple_arguments_display_arguments(self): - """ - Methods with multiple arguments should have all their arguments - displayed, but omitting 'self'. - """ - self.assertContains(self.response, "<td>baz, rox, *some_args, **some_kwargs</td>") - - def test_method_data_types(self): - """ - We should be able to get a basic idea of the type returned - by a method - """ - company = Company.objects.create(name="Django") - person = Person.objects.create(first_name="Human", last_name="User", company=company) - self.assertEqual(get_return_data_type(person.get_status_count.__name__), 'Integer') - self.assertEqual(get_return_data_type(person.get_groups_list.__name__), 'List') - - def test_descriptions_render_correctly(self): - """ - The ``description`` field should render correctly for each type of field - """ - # help text in fields - self.assertContains(self.response, "<td>first name - The person's first name</td>") - self.assertContains(self.response, "<td>last name - The person's last name</td>") - - # method docstrings - self.assertContains(self.response, "<p>Get the full name of the person</p>") - - link = '<a class="reference external" href="/admindocs/models/%s/">%s</a>' - markup = '<p>the related %s object</p>' - company_markup = markup % (link % ("admin_docs.company", "admin_docs.Company")) - - # foreign keys - self.assertContains(self.response, company_markup) - - # foreign keys with help text - self.assertContains(self.response, "%s\n - place of work" % company_markup) - - # many to many fields - self.assertContains( - self.response, - "number of related %s objects" % (link % ("admin_docs.group", "admin_docs.Group")) - ) - self.assertContains( - self.response, - "all related %s objects" % (link % ("admin_docs.group", "admin_docs.Group")) - ) - - # "raw" and "include" directives are disabled - self.assertContains(self.response, '<p>"raw" directive disabled.</p>',) - self.assertContains(self.response, '.. raw:: html\n :file: admin_docs/evilfile.txt') - self.assertContains(self.response, '<p>"include" directive disabled.</p>',) - self.assertContains(self.response, '.. include:: admin_docs/evilfile.txt') - out = self.docutils_stderr.getvalue() - self.assertIn('"raw" directive disabled', out) - self.assertIn('"include" directive disabled', out) - - def test_model_with_many_to_one(self): - link = '<a class="reference external" href="/admindocs/models/%s/">%s</a>' - response = self.client.get( - reverse('django-admindocs-models-detail', args=['admin_docs', 'company']) - ) - self.assertContains( - response, - "number of related %s objects" % (link % ("admin_docs.person", "admin_docs.Person")) - ) - self.assertContains( - response, - "all related %s objects" % (link % ("admin_docs.person", "admin_docs.Person")) - ) - - def test_model_with_no_backward_relations_render_only_relevant_fields(self): - """ - A model with ``related_name`` of `+` should not show backward relationship - links in admin docs - """ - response = self.client.get(reverse('django-admindocs-models-detail', args=['admin_docs', 'family'])) - fields = response.context_data.get('fields') - self.assertEqual(len(fields), 2) - - def test_model_docstring_renders_correctly(self): - summary = ( - '<h2 class="subhead"><p>Stores information about a person, related to <a class="reference external" ' - 'href="/admindocs/models/myapp.company/">myapp.Company</a>.</p></h2>' - ) - subheading = '<p><strong>Notes</strong></p>' - body = '<p>Use <tt class="docutils literal">save_changes()</tt> when saving this object.</p>' - model_body = ( - '<dl class="docutils"><dt><tt class="' - 'docutils literal">company</tt></dt><dd>Field storing <a class="' - 'reference external" href="/admindocs/models/myapp.company/">' - 'myapp.Company</a> where the person works.</dd></dl>' - ) - self.assertContains(self.response, 'DESCRIPTION') - self.assertContains(self.response, summary, html=True) - self.assertContains(self.response, subheading, html=True) - self.assertContains(self.response, body, html=True) - self.assertContains(self.response, model_body, html=True) - - def test_model_detail_title(self): - self.assertContains(self.response, '<h1>admin_docs.Person</h1>', html=True) - - -@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.") -class TestUtils(AdminDocsTestCase): - """ - This __doc__ output is required for testing. I copied this example from - `admindocs` documentation. (TITLE) - - Display an individual :model:`myapp.MyModel`. - - **Context** - - ``RequestContext`` - - ``mymodel`` - An instance of :model:`myapp.MyModel`. - - **Template:** - - :template:`myapp/my_template.html` (DESCRIPTION) - - some_metadata: some data - """ - - def setUp(self): - self.docstring = self.__doc__ - - def test_trim_docstring(self): - trim_docstring_output = utils.trim_docstring(self.docstring) - trimmed_docstring = ( - 'This __doc__ output is required for testing. I copied this ' - 'example from\n`admindocs` documentation. (TITLE)\n\n' - 'Display an individual :model:`myapp.MyModel`.\n\n' - '**Context**\n\n``RequestContext``\n\n``mymodel``\n' - ' An instance of :model:`myapp.MyModel`.\n\n' - '**Template:**\n\n:template:`myapp/my_template.html` ' - '(DESCRIPTION)\n\nsome_metadata: some data' - ) - self.assertEqual(trim_docstring_output, trimmed_docstring) - - def test_parse_docstring(self): - title, description, metadata = utils.parse_docstring(self.docstring) - docstring_title = ( - 'This __doc__ output is required for testing. I copied this example from\n' - '`admindocs` documentation. (TITLE)' - ) - docstring_description = ( - 'Display an individual :model:`myapp.MyModel`.\n\n' - '**Context**\n\n``RequestContext``\n\n``mymodel``\n' - ' An instance of :model:`myapp.MyModel`.\n\n' - '**Template:**\n\n:template:`myapp/my_template.html` ' - '(DESCRIPTION)' - ) - self.assertEqual(title, docstring_title) - self.assertEqual(description, docstring_description) - self.assertEqual(metadata, {'some_metadata': 'some data'}) - - def test_title_output(self): - title, description, metadata = utils.parse_docstring(self.docstring) - title_output = utils.parse_rst(title, 'model', 'model:admindocs') - self.assertIn('TITLE', title_output) - - title_rendered = ( - '<p>This __doc__ output is required for testing. I copied this ' - 'example from\n<a class="reference external" ' - 'href="/admindocs/models/admindocs/">admindocs</a> documentation. ' - '(TITLE)</p>\n' - ) - self.assertHTMLEqual(title_output, title_rendered) - - def test_description_output(self): - title, description, metadata = utils.parse_docstring(self.docstring) - description_output = utils.parse_rst(description, 'model', 'model:admindocs') - - description_rendered = ( - '<p>Display an individual <a class="reference external" ' - 'href="/admindocs/models/myapp.mymodel/">myapp.MyModel</a>.</p>\n' - '<p><strong>Context</strong></p>\n<p><tt class="docutils literal">' - 'RequestContext</tt></p>\n<dl class="docutils">\n<dt><tt class="' - 'docutils literal">mymodel</tt></dt>\n<dd>An instance of <a class="' - 'reference external" href="/admindocs/models/myapp.mymodel/">' - 'myapp.MyModel</a>.</dd>\n</dl>\n<p><strong>Template:</strong></p>' - '\n<p><a class="reference external" href="/admindocs/templates/' - 'myapp/my_template.html/">myapp/my_template.html</a> (DESCRIPTION)' - '</p>\n' - ) - self.assertHTMLEqual(description_output, description_rendered) - - def test_initial_header_level(self): - header = 'should be h3...\n\nHeader\n------\n' - output = utils.parse_rst(header, 'header') - self.assertIn('<h3>Header</h3>', output) |
