test_templates: Remove shallow template rendering code.

This code was very useful when first implemented to help catch errors
where our backend templates didn't render, but has been superceded by
the success of our URL coverage testing (which ensures every URL
supported by Zulip's urls.py is accessed by our tests, with a few
exceptions) and other tests covering all of the emails Zulip sends.

It has a significant maintenance cost because it's a bit hacky and
involves generating fake context, so it makes sense to remove these.
Any future coverage issues with templates should be addressed with a
direct test that just accessing the relevant URL or sends the relevant
email.
This commit is contained in:
Tim Abbott
2020-02-11 12:56:34 -08:00
parent 59d55d1e06
commit cb2c96f736
2 changed files with 0 additions and 234 deletions

View File

@@ -7,7 +7,6 @@ from typing import (
from django.urls.resolvers import LocaleRegexURLResolver from django.urls.resolvers import LocaleRegexURLResolver
from django.conf import settings from django.conf import settings
from django.test import override_settings from django.test import override_settings
from django.template import loader
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.db.migrations.state import StateApps from django.db.migrations.state import StateApps
from boto.s3.connection import S3Connection from boto.s3.connection import S3Connection
@@ -443,36 +442,6 @@ def write_instrumentation_reports(full_suite: bool, include_webhooks: bool) -> N
print(" %s" % (untested_pattern,)) print(" %s" % (untested_pattern,))
sys.exit(1) sys.exit(1)
def get_all_templates() -> List[str]:
templates = []
relpath = os.path.relpath
isfile = os.path.isfile
path_exists = os.path.exists
def is_valid_template(p: str, n: str) -> bool:
return 'webhooks' not in p \
and not n.startswith('.') \
and not n.startswith('__init__') \
and not n.endswith('.md') \
and not n.endswith('.source.html') \
and isfile(p)
def process(template_dir: str, dirname: str, fnames: Iterable[str]) -> None:
for name in fnames:
path = os.path.join(dirname, name)
if is_valid_template(path, name):
templates.append(relpath(path, template_dir))
for engine in loader.engines.all():
template_dirs = [d for d in engine.template_dirs if path_exists(d)]
for template_dir in template_dirs:
template_dir = os.path.normpath(template_dir)
for dirpath, dirnames, fnames in os.walk(template_dir):
process(template_dir, dirpath, fnames)
return templates
def load_subdomain_token(response: HttpResponse) -> Dict[str, Any]: def load_subdomain_token(response: HttpResponse) -> Dict[str, Any]:
assert isinstance(response, HttpResponseRedirect) assert isinstance(response, HttpResponseRedirect)
token = response.url.rsplit('/', 1)[1] token = response.url.rsplit('/', 1)[1]

View File

@@ -1,217 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import mock
import re
from typing import Any, Dict, Iterable
import logging
from django.contrib.auth.models import AnonymousUser
from django.test import override_settings
from django.template.loader import get_template from django.template.loader import get_template
from django.test.client import RequestFactory
from jinja2.exceptions import UndefinedError
from zerver.context_processors import login_context
from zerver.lib.exceptions import InvalidMarkdownIncludeStatement from zerver.lib.exceptions import InvalidMarkdownIncludeStatement
from zerver.lib.test_helpers import get_all_templates
from zerver.lib.test_classes import ( from zerver.lib.test_classes import (
ZulipTestCase, ZulipTestCase,
) )
from zerver.lib.test_runner import slow
from zerver.models import Realm
class get_form_value:
def __init__(self, value: Any) -> None:
self._value = value
def value(self) -> Any:
return self._value
class DummyForm(Dict[str, Any]):
pass
class TemplateTestCase(ZulipTestCase): class TemplateTestCase(ZulipTestCase):
"""
Tests that backend template rendering doesn't crash.
This renders all the Zulip backend templates, passing dummy data
as the context, which allows us to verify whether any of the
templates are broken enough to not render at all (no verification
is done that the output looks right). Please see `get_context`
function documentation for more information.
"""
@slow("Tests a large number of different templates")
@override_settings(TERMS_OF_SERVICE=None)
def test_templates(self) -> None:
# Just add the templates whose context has a conflict with other
# templates' context in `defer`.
defer = ['analytics/activity.html']
# Django doesn't send template_rendered signal for parent templates
# https://code.djangoproject.com/ticket/24622
covered = [
'zerver/portico.html',
'zerver/portico_signup.html',
]
logged_out = [
'zerver/compare.html',
'zerver/footer.html',
]
logged_in = [
'analytics/stats.html',
'zerver/landing_nav.html',
]
unusual = [
'zerver/emails/confirm_new_email.subject.txt',
'zerver/emails/compiled/confirm_new_email.html',
'zerver/emails/confirm_new_email.txt',
'zerver/emails/notify_change_in_email.subject.txt',
'zerver/emails/compiled/notify_change_in_email.html',
'zerver/emails/digest.subject.txt',
'zerver/emails/digest.txt',
'zerver/emails/followup_day1.subject.txt',
'zerver/emails/compiled/followup_day1.html',
'zerver/emails/followup_day1.txt',
'zerver/emails/followup_day2.subject.txt',
'zerver/emails/followup_day2.txt',
'zerver/emails/compiled/followup_day2.html',
'zerver/emails/compiled/password_reset.html',
'corporate/zephyr.html',
'corporate/zephyr-mirror.html',
'zilencer/enterprise_tos_accept_body.txt',
'zerver/zulipchat_migration_tos.html',
'zilencer/enterprise_tos_accept_body.txt',
'zerver/invalid_email.html',
'zerver/invalid_realm.html',
'zerver/debug.html',
'zerver/base.html',
'zerver/portico-header.html',
'two_factor/_wizard_forms.html',
]
integrations_regexp = re.compile(r'^zerver/integrations/.*\.html$')
# Since static/generated/bots/ is searched by Jinja2 for templates,
# it mistakes logo files under that directory for templates.
bot_logos_regexp = re.compile(r'\w+\/logo\.(svg|png)$')
skip = covered + defer + logged_out + logged_in + unusual + ['tests/test_markdown.html',
'zerver/terms.html',
'zerver/privacy.html']
all_templates = get_all_templates()
self.assertEqual(set(skip) - set(all_templates), set())
templates = [t for t in all_templates if not (
t in skip or integrations_regexp.match(t) or bot_logos_regexp.match(t))]
self.render_templates(templates, self.get_context())
# Test the deferred templates with updated context.
update = {'data': [('one', 'two')]}
self.render_templates(defer, self.get_context(**update))
def render_templates(self, templates: Iterable[str], context: Dict[str, Any]) -> None:
for template_name in templates:
template = get_template(template_name)
try:
template.render(context)
except UndefinedError as e: # nocoverage # ideally, this block shouldn't have to execute
raise UndefinedError(e.message + """\n
This test is designed to confirm that every Jinja2 template is free
of syntax errors. There are two common causes for this test failing:
* One of Zulip's HTML templates doesn't render.
* A new context variable was added to a template, without a sample
value being added to `get_context` in zerver/tests/test_templates.py.
""")
except Exception: # nocoverage # nicer error handler
logging.error("Exception while rendering '{}'".format(template.template.name))
raise
def get_context(self, **kwargs: Any) -> Dict[str, Any]:
"""Get the dummy context for shallow testing.
The context returned will always contain a parameter called
`shallow_tested`, which tells the signal receiver that the
test was not rendered in an actual logical test (so we can
still do coverage reporting on which templates have a logical
test).
Note: `context` just holds dummy values used to make the test
pass. This context only ensures that the templates do not
throw a 500 error when rendered using dummy data. If new
required parameters are added to a template, this test will
fail; the usual fix is to just update the context below to add
the new parameter to the dummy data.
:param kwargs: Keyword arguments can be used to update the base
context.
"""
user_profile = self.example_user('hamlet')
realm = user_profile.realm
email = user_profile.email
context = dict(
sidebar_index="zerver/help/include/sidebar_index.md",
doc_root="/help/",
article="zerver/help/index.md",
shallow_tested=True,
user_profile=user_profile,
user=user_profile,
form=DummyForm(
full_name=get_form_value('John Doe'),
terms=get_form_value(True),
email=get_form_value(email),
emails=get_form_value(email),
subdomain=get_form_value("zulip"),
next_param=get_form_value("billing")
),
current_url=lambda: 'www.zulip.com',
integrations_dict={},
referrer=dict(
full_name='John Doe',
realm=dict(name='zulip.com'),
),
message_count=0,
messages=[dict(header='Header')],
new_streams=dict(html=''),
data=dict(title='Title'),
device_info={"device_browser": "Chrome",
"device_os": "Windows",
"device_ip": "127.0.0.1",
"login_time": "9:33am NewYork, NewYork",
},
api_uri_context={},
realm_plan_type=Realm.LIMITED,
cloud_annual_price=80,
seat_count=8,
request=RequestFactory().get("/"),
invite_as={"MEMBER": 1},
max_file_upload_size = 25,
avatar_urls={"john@gmail.com": "www.zulip.com"},
realm_admin_emails=lambda _: "admin emails",
get_discount_for_realm=lambda _: 0,
realm_icon_url=lambda _: "url",
realm=realm,
)
# A necessary block to make login_context available to templates.
request = mock.MagicMock()
request.user = AnonymousUser()
request.realm = realm
context.update(login_context(request))
context.update(kwargs)
return context
def test_markdown_in_template(self) -> None: def test_markdown_in_template(self) -> None:
template = get_template("tests/test_markdown.html") template = get_template("tests/test_markdown.html")
context = { context = {