2016-08-04 00:35:53 +02:00
|
|
|
import sys
|
|
|
|
import unittest
|
2020-06-11 00:54:34 +02:00
|
|
|
from typing import Optional
|
2016-08-04 00:35:53 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
from tools.lib.template_parser import (
|
2016-08-31 00:43:08 +02:00
|
|
|
TemplateParserException,
|
2016-08-04 00:35:53 +02:00
|
|
|
is_django_block_tag,
|
2016-08-08 01:46:44 +02:00
|
|
|
tokenize,
|
2016-08-04 00:35:53 +02:00
|
|
|
validate,
|
|
|
|
)
|
|
|
|
except ImportError:
|
2021-02-12 08:20:45 +01:00
|
|
|
print("ERROR!!! You need to run this via tools/test-tools.")
|
2016-08-04 00:35:53 +02:00
|
|
|
sys.exit(1)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-08-04 00:35:53 +02:00
|
|
|
class ParserTest(unittest.TestCase):
|
2021-02-12 08:19:30 +01:00
|
|
|
def _assert_validate_error(
|
|
|
|
self,
|
|
|
|
error: str,
|
|
|
|
fn: Optional[str] = None,
|
|
|
|
text: Optional[str] = None,
|
|
|
|
) -> None:
|
2017-08-07 02:01:59 +02:00
|
|
|
with self.assertRaisesRegex(TemplateParserException, error):
|
2021-11-24 14:25:30 +01:00
|
|
|
validate(fn=fn, text=text)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_is_django_block_tag(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertTrue(is_django_block_tag("block"))
|
|
|
|
self.assertFalse(is_django_block_tag("not a django tag"))
|
2016-08-04 00:35:53 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_vanilla_html(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
"""
|
2016-08-04 00:35:53 +02:00
|
|
|
Verify that validate() does not raise errors for
|
|
|
|
well-formed HTML.
|
2021-02-12 08:19:30 +01:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-04 00:35:53 +02:00
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<td>foo</td>
|
|
|
|
</tr>
|
2021-02-12 08:20:45 +01:00
|
|
|
</table>"""
|
2016-08-04 00:35:53 +02:00
|
|
|
validate(text=my_html)
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_handlebars(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-08 16:26:33 +02:00
|
|
|
{{#with stream}}
|
|
|
|
<p>{{stream}}</p>
|
|
|
|
{{/with}}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2016-08-08 16:26:33 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_comment(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2017-02-03 04:26:35 +01:00
|
|
|
<!---
|
|
|
|
<h1>foo</h1>
|
2021-02-12 08:20:45 +01:00
|
|
|
-->"""
|
2017-02-03 04:26:35 +01:00
|
|
|
validate(text=my_html)
|
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_django(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-08 16:30:07 +02:00
|
|
|
{% include "some_other.html" %}
|
|
|
|
{% if foo %}
|
|
|
|
<p>bar</p>
|
|
|
|
{% endif %}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2016-08-08 16:30:07 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2017-07-19 21:04:34 +02:00
|
|
|
{% block "content" %}
|
|
|
|
{% with className="class" %}
|
|
|
|
{% include 'foobar' %}
|
|
|
|
{% endwith %}
|
|
|
|
{% endblock %}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2017-07-19 21:04:34 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_no_start_tag(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
foo</p>
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
|
|
|
self._assert_validate_error("No start tag", text=my_html)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_mismatched_tag(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
<b>foo</i>
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
|
|
|
self._assert_validate_error("Mismatched tag.", text=my_html)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_bad_indentation(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
<p>
|
|
|
|
foo
|
|
|
|
</p>
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2021-11-24 14:25:30 +01:00
|
|
|
self._assert_validate_error("Bad indentation.", text=my_html)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_state_depth(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:57:37 +02:00
|
|
|
<b>
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
|
|
|
self._assert_validate_error("Missing end tag", text=my_html)
|
2016-08-31 00:57:37 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_incomplete_handlebars_tag_1(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
{{# foo
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
self._assert_validate_error(
|
2021-05-10 07:02:14 +02:00
|
|
|
'''Tag missing "}}" at line 2 col 13:"{{# foo
|
2021-02-12 08:19:30 +01:00
|
|
|
"''',
|
|
|
|
text=my_html,
|
|
|
|
)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_incomplete_handlebars_tag_2(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
{{# foo }
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2021-05-10 07:02:14 +02:00
|
|
|
self._assert_validate_error('Tag missing "}}" at line 2 col 13:"{{# foo }\n"', text=my_html)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_incomplete_django_tag_1(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
{% foo
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
self._assert_validate_error(
|
2021-05-10 07:02:14 +02:00
|
|
|
'''Tag missing "%}" at line 2 col 13:"{% foo
|
2021-02-12 08:19:30 +01:00
|
|
|
"''',
|
|
|
|
text=my_html,
|
|
|
|
)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_incomplete_django_tag_2(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
{% foo %
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2021-05-10 07:02:14 +02:00
|
|
|
self._assert_validate_error('Tag missing "%}" at line 2 col 13:"{% foo %\n"', text=my_html)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_incomplete_html_tag_1(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
<b
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
self._assert_validate_error(
|
2021-05-10 07:02:14 +02:00
|
|
|
'''Tag missing ">" at line 2 col 13:"<b
|
2021-02-12 08:19:30 +01:00
|
|
|
"''',
|
|
|
|
text=my_html,
|
|
|
|
)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_incomplete_html_tag_2(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:43:08 +02:00
|
|
|
<a href="
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
|
|
|
my_html1 = """
|
2017-02-21 13:53:52 +01:00
|
|
|
<a href=""
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
self._assert_validate_error(
|
2021-05-10 07:02:14 +02:00
|
|
|
'''Tag missing ">" at line 2 col 13:"<a href=""
|
2021-02-12 08:19:30 +01:00
|
|
|
"''',
|
|
|
|
text=my_html1,
|
|
|
|
)
|
|
|
|
self._assert_validate_error(
|
2021-05-10 07:02:14 +02:00
|
|
|
'''Unbalanced quotes at line 2 col 13:"<a href="
|
2021-02-12 08:19:30 +01:00
|
|
|
"''',
|
|
|
|
text=my_html,
|
|
|
|
)
|
2016-08-31 00:43:08 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_validate_empty_html_tag(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-31 00:54:41 +02:00
|
|
|
< >
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
|
|
|
self._assert_validate_error("Tag name missing", text=my_html)
|
2016-08-31 00:54:41 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_code_blocks(self) -> None:
|
2016-08-18 16:02:18 +02:00
|
|
|
|
|
|
|
# This is fine.
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-18 16:02:18 +02:00
|
|
|
<code>
|
|
|
|
x = 5
|
|
|
|
y = x + 1
|
2021-02-12 08:20:45 +01:00
|
|
|
</code>"""
|
2016-08-18 16:02:18 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
|
|
|
# This is also fine.
|
|
|
|
my_html = "<code>process_widgets()</code>"
|
|
|
|
validate(text=my_html)
|
|
|
|
|
|
|
|
# This is illegal.
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-18 16:02:18 +02:00
|
|
|
<code>x =
|
|
|
|
5</code>
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
|
|
|
self._assert_validate_error("Code tag is split across two lines.", text=my_html)
|
2016-08-18 16:02:18 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_anchor_blocks(self) -> None:
|
2016-08-18 16:17:06 +02:00
|
|
|
|
|
|
|
# This is allowed, although strange.
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-18 16:17:06 +02:00
|
|
|
<a hef="/some/url">
|
|
|
|
Click here
|
|
|
|
for more info.
|
2021-02-12 08:20:45 +01:00
|
|
|
</a>"""
|
2016-08-18 16:17:06 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
|
|
|
# This is fine.
|
|
|
|
my_html = '<a href="/some/url">click here</a>'
|
|
|
|
validate(text=my_html)
|
|
|
|
|
|
|
|
# Even this is fine.
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2016-08-18 16:17:06 +02:00
|
|
|
<a class="twitter-timeline" href="https://twitter.com/ZulipStatus"
|
|
|
|
data-widget-id="443457763394334720"
|
|
|
|
data-screen-name="ZulipStatus"
|
|
|
|
>@ZulipStatus on Twitter</a>.
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2016-08-18 16:17:06 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
2020-01-16 21:12:02 +01:00
|
|
|
def test_validate_jinja2_whitespace_markers_1(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2019-04-16 12:54:01 +02:00
|
|
|
{% if foo -%}
|
|
|
|
this is foo
|
2020-01-16 21:12:02 +01:00
|
|
|
{% endif %}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2019-04-16 12:54:01 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
2020-01-16 21:12:02 +01:00
|
|
|
def test_validate_jinja2_whitespace_markers_2(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2019-04-16 12:54:01 +02:00
|
|
|
{% if foo %}
|
|
|
|
this is foo
|
|
|
|
{%- endif %}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2020-01-16 21:12:02 +01:00
|
|
|
validate(text=my_html)
|
2019-04-16 12:54:01 +02:00
|
|
|
|
2020-01-16 21:12:02 +01:00
|
|
|
def test_validate_jinja2_whitespace_markers_3(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2020-01-16 21:12:02 +01:00
|
|
|
{% if foo %}
|
2019-04-16 12:54:01 +02:00
|
|
|
this is foo
|
2020-01-16 21:12:02 +01:00
|
|
|
{% endif -%}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2020-01-16 21:12:02 +01:00
|
|
|
validate(text=my_html)
|
2019-04-16 12:54:01 +02:00
|
|
|
|
2020-01-16 21:12:02 +01:00
|
|
|
def test_validate_jinja2_whitespace_markers_4(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2020-01-16 21:12:02 +01:00
|
|
|
{%- if foo %}
|
2019-04-16 12:54:01 +02:00
|
|
|
this is foo
|
|
|
|
{% endif %}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2019-04-16 12:54:01 +02:00
|
|
|
validate(text=my_html)
|
|
|
|
|
2020-01-16 21:12:02 +01:00
|
|
|
def test_validate_mismatch_jinja2_whitespace_markers_1(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2020-01-16 21:12:02 +01:00
|
|
|
{% if foo %}
|
2019-04-16 12:54:01 +02:00
|
|
|
this is foo
|
2020-01-16 21:12:02 +01:00
|
|
|
{%- if bar %}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
|
|
|
self._assert_validate_error("Missing end tag", text=my_html)
|
2019-04-16 12:54:01 +02:00
|
|
|
|
2020-01-16 21:12:02 +01:00
|
|
|
def test_validate_jinja2_whitespace_type2_markers(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
my_html = """
|
2020-01-16 21:12:02 +01:00
|
|
|
{%- if foo -%}
|
2019-04-16 12:54:01 +02:00
|
|
|
this is foo
|
|
|
|
{% endif %}
|
2021-02-12 08:20:45 +01:00
|
|
|
"""
|
2020-01-16 21:12:02 +01:00
|
|
|
validate(text=my_html)
|
2019-04-16 12:54:01 +02:00
|
|
|
|
2017-12-13 06:45:46 +01:00
|
|
|
def test_tokenize(self) -> None:
|
2021-04-21 00:46:14 +02:00
|
|
|
tag = "<!DOCTYPE html>"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-04-21 00:46:14 +02:00
|
|
|
self.assertEqual(token.kind, "html_doctype")
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "<a>bla"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "html_start")
|
|
|
|
self.assertEqual(token.tag, "a")
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2021-04-21 00:46:14 +02:00
|
|
|
tag = "<br />bla"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "html_singleton")
|
|
|
|
self.assertEqual(token.tag, "br")
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "<input>bla"
|
2018-02-15 22:16:40 +01:00
|
|
|
token = tokenize(tag)[0]
|
2021-04-21 00:46:14 +02:00
|
|
|
self.assertEqual(token.kind, "html_start") # We later mark this an error.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.tag, "input")
|
2018-02-15 22:16:40 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "<input />bla"
|
2018-02-15 22:16:40 +01:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "html_singleton")
|
|
|
|
self.assertEqual(token.tag, "input")
|
2018-02-15 22:16:40 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "</a>bla"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "html_end")
|
|
|
|
self.assertEqual(token.tag, "a")
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "{{#with foo}}bla"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "handlebars_start")
|
|
|
|
self.assertEqual(token.tag, "with")
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "{{/with}}bla"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "handlebars_end")
|
|
|
|
self.assertEqual(token.tag, "with")
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "{% if foo %}bla"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "django_start")
|
|
|
|
self.assertEqual(token.tag, "if")
|
2016-08-08 01:46:44 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "{% endif %}bla"
|
2016-08-08 01:46:44 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "django_end")
|
|
|
|
self.assertEqual(token.tag, "if")
|
2019-04-16 12:54:01 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "{% if foo -%}bla"
|
2019-04-16 12:54:01 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "jinja2_whitespace_stripped_start")
|
|
|
|
self.assertEqual(token.tag, "if")
|
2019-04-16 12:54:01 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "{%- endif %}bla"
|
2019-04-16 12:54:01 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "jinja2_whitespace_stripped_end")
|
|
|
|
self.assertEqual(token.tag, "if")
|
2019-04-16 12:54:01 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
tag = "{%- if foo -%}bla"
|
2019-04-16 12:54:01 +02:00
|
|
|
token = tokenize(tag)[0]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(token.kind, "jinja2_whitespace_stripped_type2_start")
|
|
|
|
self.assertEqual(token.tag, "if")
|