diff --git a/django/contrib/admindocs/tests/__init__.py b/django/contrib/admindocs/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/django/contrib/admindocs/tests/test_fields.py b/django/contrib/admindocs/tests/test_fields.py deleted file mode 100644 index 3f4efedb9c..0000000000 --- a/django/contrib/admindocs/tests/test_fields.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import unicode_literals - -import unittest - -from django.contrib.admindocs import views -from django.db import models -from django.db.models import fields -from django.utils.translation import ugettext as _ - - -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): - 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: %(field_type)s') % { - 'field_type': 'DescriptionLackingField' - } - ) 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 = ( + '

This __doc__ output is required for testing. I copied this ' + 'example from\nadmindocs documentation. ' + '(TITLE)

\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 = ( + '

Display an individual myapp.MyModel.

\n' + '

Context

\n

' + 'RequestContext

\n
\n
mymodel
\n
An instance of ' + 'myapp.MyModel.
\n
\n

Template:

' + '\n

myapp/my_template.html (DESCRIPTION)' + '

\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('

Header

', output) + + def test_parse_rst(self): + """ + parse_rst() should use `cmsreference` as the default role. + """ + markup = '

title

\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 = '

reST, interpreted text, default role.

\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, '

Documentation

', html=True) + self.assertContains(response, '

Django administration

') + self.client.logout() + response = self.client.get(reverse('django-admindocs-docroot'), follow=True) + # Should display the login screen + self.assertContains(response, '', 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, '

extends

', html=True) + + def test_templatefilter_index(self): + response = self.client.get(reverse('django-admindocs-filters')) + self.assertContains(response, '

first

', html=True) + + def test_view_index(self): + response = self.client.get(reverse('django-admindocs-views-index')) + self.assertContains( + response, + '

/admindocs/

', + html=True + ) + self.assertContains(response, 'Views by namespace test') + self.assertContains(response, 'Name: test:func.') + + @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, + '

/admin/

', + 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, '

admin_docs.views.XViewClass

') + + 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, + '

Authentication and Authorization (django.contrib.auth)

', + html=True + ) + + def test_template_detail(self): + response = self.client.get(reverse('django-admindocs-templates', args=['admin_doc/template_detail.html'])) + self.assertContains(response, '

Template: "admin_doc/template_detail.html"

', html=True) + + def test_missing_docutils(self): + utils.docutils_is_available = False + try: + response = self.client.get(reverse('django-admindocs-docroot')) + self.assertContains( + response, + '

The admin documentation system requires Python\'s ' + 'docutils library.

', + html=True + ) + self.assertContains(response, '

Django administration

') + 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, 'Template filters', 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, 'Template tags', 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, "get_full_name") + self.assertNotContains(self.response, "_get_full_name") + self.assertNotContains(self.response, "add_image") + self.assertNotContains(self.response, "delete_image") + self.assertNotContains(self.response, "set_status") + self.assertNotContains(self.response, "save_changes") + + def test_methods_with_arguments(self): + """ + Methods that take arguments should also displayed. + """ + self.assertContains(self.response, "

Methods with arguments

") + self.assertContains(self.response, "rename_company") + self.assertContains(self.response, "dummy_function") + self.assertContains(self.response, "suffix_company_name") + + def test_methods_with_arguments_display_arguments(self): + """ + Methods with arguments should have their arguments displayed. + """ + self.assertContains(self.response, "new_name") + + def test_methods_with_arguments_display_arguments_default_value(self): + """ + Methods with keyword arguments should have their arguments displayed. + """ + self.assertContains(self.response, "suffix='ltd'") + + 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, "baz, rox, *some_args, **some_kwargs") + + 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, "first name - The person's first name") + self.assertContains(self.response, "last name - The person's last name") + + # method docstrings + self.assertContains(self.response, "

Get the full name of the person

") + + link = '%s' + markup = '

the related %s object

' + 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, '

"raw" directive disabled.

',) + self.assertContains(self.response, '.. raw:: html\n :file: admin_docs/evilfile.txt') + self.assertContains(self.response, '

"include" directive disabled.

',) + 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 = '%s' + 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 = ( + '

Stores information about a person, related to myapp.Company.

' + ) + subheading = '

Notes

' + body = '

Use save_changes() when saving this object.

' + model_body = ( + '
company
Field storing ' + 'myapp.Company where the person works.
' + ) + 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, '

admin_docs.Person

', 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\w+)/b/(?P\w+)/$', '//b//'), + (r'^(?P\w+)/b/(?P\w+)$', '//b/'), + ) + 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, '', html=True) - self.client.force_login(self.superuser) - response = self.client.get(reverse('django-admindocs-docroot')) - self.assertContains(response, '

Documentation

', html=True) - self.assertContains(response, '

Django administration

') - - 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, '

extends

', html=True) - - def test_templatefilter_index(self): - response = self.client.get(reverse('django-admindocs-filters')) - self.assertContains(response, '

first

', html=True) - - def test_view_index(self): - response = self.client.get(reverse('django-admindocs-views-index')) - self.assertContains( - response, - '

/admindocs/

', - html=True - ) - self.assertContains(response, 'Views by namespace test') - self.assertContains(response, 'Name: test:func.') - - @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, - '

/admin/

', - 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, '

admin_docs.views.XViewClass

') - - 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, - '

Authentication and Authorization (django.contrib.auth)

', - html=True - ) - - def test_template_detail(self): - response = self.client.get(reverse('django-admindocs-templates', args=['admin_doc/template_detail.html'])) - self.assertContains(response, '

Template: "admin_doc/template_detail.html"

', html=True) - - def test_missing_docutils(self): - utils.docutils_is_available = False - try: - response = self.client.get(reverse('django-admindocs-docroot')) - self.assertContains( - response, - '

The admin documentation system requires Python\'s ' - 'docutils library.

', - html=True - ) - self.assertContains(response, '

Django administration

') - finally: - utils.docutils_is_available = True - - def test_simplify_regex(self): - tests = ( - ('^a', '/a'), - (r'^(?P\w+)/b/(?P\w+)/$', '//b//'), - (r'^(?P\w+)/b/(?P\w+)$', '//b/'), - ) - 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, 'Template filters', 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, 'Template tags', 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 = '

title

\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 = '

reST, interpreted text, default role.

\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, "get_full_name") - self.assertNotContains(self.response, "_get_full_name") - self.assertNotContains(self.response, "add_image") - self.assertNotContains(self.response, "delete_image") - self.assertNotContains(self.response, "set_status") - self.assertNotContains(self.response, "save_changes") - - def test_methods_with_arguments(self): - """ - Methods that take arguments should also displayed. - """ - self.assertContains(self.response, "

Methods with arguments

") - self.assertContains(self.response, "rename_company") - self.assertContains(self.response, "dummy_function") - self.assertContains(self.response, "suffix_company_name") - - def test_methods_with_arguments_display_arguments(self): - """ - Methods with arguments should have their arguments displayed. - """ - self.assertContains(self.response, "new_name") - - def test_methods_with_arguments_display_arguments_default_value(self): - """ - Methods with keyword arguments should have their arguments displayed. - """ - self.assertContains(self.response, "suffix='ltd'") - - 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, "baz, rox, *some_args, **some_kwargs") - - 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, "first name - The person's first name") - self.assertContains(self.response, "last name - The person's last name") - - # method docstrings - self.assertContains(self.response, "

Get the full name of the person

") - - link = '%s' - markup = '

the related %s object

' - 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, '

"raw" directive disabled.

',) - self.assertContains(self.response, '.. raw:: html\n :file: admin_docs/evilfile.txt') - self.assertContains(self.response, '

"include" directive disabled.

',) - 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 = '%s' - 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 = ( - '

Stores information about a person, related to myapp.Company.

' - ) - subheading = '

Notes

' - body = '

Use save_changes() when saving this object.

' - model_body = ( - '
company
Field storing ' - 'myapp.Company where the person works.
' - ) - 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, '

admin_docs.Person

', 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 = ( - '

This __doc__ output is required for testing. I copied this ' - 'example from\nadmindocs documentation. ' - '(TITLE)

\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 = ( - '

Display an individual myapp.MyModel.

\n' - '

Context

\n

' - 'RequestContext

\n
\n
mymodel
\n
An instance of ' - 'myapp.MyModel.
\n
\n

Template:

' - '\n

myapp/my_template.html (DESCRIPTION)' - '

\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('

Header

', output)