Files
zulip/tools/tests/test_template_parser.py
Steve Howell 951514dd7d check-templates: Be stricter about singleton tags.
We now forbid tags of the form `<foo ... />` in most
places, and we also forbid it even for several void
tags.

We make exceptions for tags that are already formatted
in two different ways in our codebase.  This is mostly
svg tags, plus these common cases:

    - br
    - hr
    - img
    - input

It would be nice to lock down a convention for these,
even though the HTML specification is unopinionated
on these.  We'll probably want to stay flexible for
svg tags, since they are sometimes copy/pasted from
other sources (although it's probably rare enough for
them that we can tolerate just doing minor edits as
needed).
2020-04-24 20:25:55 +00:00

306 lines
8.8 KiB
Python

from typing import Optional
import sys
import unittest
try:
from tools.lib.template_parser import (
TemplateParserException,
is_django_block_tag,
tokenize,
validate,
)
except ImportError:
print('ERROR!!! You need to run this via tools/test-tools.')
sys.exit(1)
class ParserTest(unittest.TestCase):
def _assert_validate_error(self, error: str, fn: Optional[str]=None,
text: Optional[str]=None, check_indent: bool=True) -> None:
with self.assertRaisesRegex(TemplateParserException, error):
validate(fn=fn, text=text, check_indent=check_indent)
def test_is_django_block_tag(self) -> None:
self.assertTrue(is_django_block_tag('block'))
self.assertFalse(is_django_block_tag('not a django tag'))
def test_validate_vanilla_html(self) -> None:
'''
Verify that validate() does not raise errors for
well-formed HTML.
'''
my_html = '''
<table>
<tr>
<td>foo</td>
</tr>
</table>'''
validate(text=my_html)
def test_validate_handlebars(self) -> None:
my_html = '''
{{#with stream}}
<p>{{stream}}</p>
{{/with}}
'''
validate(text=my_html)
def test_validate_comment(self) -> None:
my_html = '''
<!---
<h1>foo</h1>
-->'''
validate(text=my_html)
def test_validate_django(self) -> None:
my_html = '''
{% include "some_other.html" %}
{% if foo %}
<p>bar</p>
{% endif %}
'''
validate(text=my_html)
my_html = '''
{% block "content" %}
{% with className="class" %}
{% include 'foobar' %}
{% endwith %}
{% endblock %}
'''
validate(text=my_html)
def test_validate_no_start_tag(self) -> None:
my_html = '''
foo</p>
'''
self._assert_validate_error('No start tag', text=my_html)
def test_validate_mismatched_tag(self) -> None:
my_html = '''
<b>foo</i>
'''
self._assert_validate_error('Mismatched tag.', text=my_html)
def test_validate_bad_indentation(self) -> None:
my_html = '''
<p>
foo
</p>
'''
self._assert_validate_error('Bad indentation.', text=my_html, check_indent=True)
def test_validate_state_depth(self) -> None:
my_html = '''
<b>
'''
self._assert_validate_error('Missing end tag', text=my_html)
def test_validate_incomplete_handlebars_tag_1(self) -> None:
my_html = '''
{{# foo
'''
self._assert_validate_error('''Tag missing "}}" at Line 2 Col 13:"{{# foo
"''', text=my_html)
def test_validate_incomplete_handlebars_tag_2(self) -> None:
my_html = '''
{{# foo }
'''
self._assert_validate_error('Tag missing "}}" at Line 2 Col 13:"{{# foo }\n"', text=my_html)
def test_validate_incomplete_django_tag_1(self) -> None:
my_html = '''
{% foo
'''
self._assert_validate_error('''Tag missing "%}" at Line 2 Col 13:"{% foo
"''', text=my_html)
def test_validate_incomplete_django_tag_2(self) -> None:
my_html = '''
{% foo %
'''
self._assert_validate_error('Tag missing "%}" at Line 2 Col 13:"{% foo %\n"', text=my_html)
def test_validate_incomplete_html_tag_1(self) -> None:
my_html = '''
<b
'''
self._assert_validate_error('''Tag missing ">" at Line 2 Col 13:"<b
"''', text=my_html)
def test_validate_incomplete_html_tag_2(self) -> None:
my_html = '''
<a href="
'''
my_html1 = '''
<a href=""
'''
self._assert_validate_error('''Tag missing ">" at Line 2 Col 13:"<a href=""
"''', text=my_html1)
self._assert_validate_error('''Unbalanced Quotes at Line 2 Col 13:"<a href="
"''', text=my_html)
def test_validate_empty_html_tag(self) -> None:
my_html = '''
< >
'''
self._assert_validate_error('Tag name missing', text=my_html)
def test_code_blocks(self) -> None:
# This is fine.
my_html = '''
<code>
x = 5
y = x + 1
</code>'''
validate(text=my_html)
# This is also fine.
my_html = "<code>process_widgets()</code>"
validate(text=my_html)
# This is illegal.
my_html = '''
<code>x =
5</code>
'''
self._assert_validate_error('Code tag is split across two lines.', text=my_html)
def test_anchor_blocks(self) -> None:
# This is allowed, although strange.
my_html = '''
<a hef="/some/url">
Click here
for more info.
</a>'''
validate(text=my_html)
# This is fine.
my_html = '<a href="/some/url">click here</a>'
validate(text=my_html)
# Even this is fine.
my_html = '''
<a class="twitter-timeline" href="https://twitter.com/ZulipStatus"
data-widget-id="443457763394334720"
data-screen-name="ZulipStatus"
>@ZulipStatus on Twitter</a>.
'''
validate(text=my_html)
def test_validate_jinja2_whitespace_markers_1(self) -> None:
my_html = '''
{% if foo -%}
this is foo
{% endif %}
'''
validate(text=my_html)
def test_validate_jinja2_whitespace_markers_2(self) -> None:
my_html = '''
{% if foo %}
this is foo
{%- endif %}
'''
validate(text=my_html)
def test_validate_jinja2_whitespace_markers_3(self) -> None:
my_html = '''
{% if foo %}
this is foo
{% endif -%}
'''
validate(text=my_html)
def test_validate_jinja2_whitespace_markers_4(self) -> None:
my_html = '''
{%- if foo %}
this is foo
{% endif %}
'''
validate(text=my_html)
def test_validate_mismatch_jinja2_whitespace_markers_1(self) -> None:
my_html = '''
{% if foo %}
this is foo
{%- if bar %}
'''
self._assert_validate_error('Missing end tag', text=my_html)
def test_validate_jinja2_whitespace_type2_markers(self) -> None:
my_html = '''
{%- if foo -%}
this is foo
{% endif %}
'''
validate(text=my_html)
def test_tokenize(self) -> None:
tag = '<meta whatever>bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'html_special')
tag = '<a>bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'html_start')
self.assertEqual(token.tag, 'a')
tag = '<br>bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'html_singleton')
self.assertEqual(token.tag, 'br')
tag = '<input>bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'html_singleton')
self.assertEqual(token.tag, 'input')
tag = '<input />bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'html_singleton')
self.assertEqual(token.tag, 'input')
tag = '</a>bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'html_end')
self.assertEqual(token.tag, 'a')
tag = '{{#with foo}}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'handlebars_start')
self.assertEqual(token.tag, 'with')
tag = '{{/with}}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'handlebars_end')
self.assertEqual(token.tag, 'with')
tag = '{% if foo %}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'django_start')
self.assertEqual(token.tag, 'if')
tag = '{% endif %}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'django_end')
self.assertEqual(token.tag, 'if')
tag = '{% if foo -%}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'jinja2_whitespace_stripped_start')
self.assertEqual(token.tag, 'if')
tag = '{%- endif %}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'jinja2_whitespace_stripped_end')
self.assertEqual(token.tag, 'if')
tag = '{%- if foo -%}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'jinja2_whitespace_stripped_type2_start')
self.assertEqual(token.tag, 'if')