zulip/tools/tests/test_css_parser.py

167 lines
4.9 KiB
Python

from typing import cast, Any
import sys
import unittest
try:
from tools.lib.css_parser import (
CssParserException,
CssSection,
parse,
)
except ImportError:
print('ERROR!!! You need to run this via tools/test-tools.')
sys.exit(1)
class ParserTestHappyPath(unittest.TestCase):
def test_basic_parse(self) -> None:
my_selector = 'li.foo'
my_block = '''{
color: red;
}'''
my_css = my_selector + ' ' + my_block
res = parse(my_css)
self.assertEqual(res.text().strip(), 'li.foo {\n color: red;\n}')
section = cast(CssSection, res.sections[0])
block = section.declaration_block
self.assertEqual(block.text().strip(), '{\n color: red;\n}')
declaration = block.declarations[0]
self.assertEqual(declaration.css_property, 'color')
self.assertEqual(declaration.css_value.text().strip(), 'red')
def test_same_line_comment(self) -> None:
my_css = '''
li.hide {
display: none; /* comment here */
/* Not to be confused
with this comment */
color: green;
}'''
res = parse(my_css)
section = cast(CssSection, res.sections[0])
block = section.declaration_block
declaration = block.declarations[0]
self.assertIn('/* comment here */', declaration.text())
def test_no_semicolon(self) -> None:
my_css = '''
p { color: red }
'''
reformatted_css = 'p {\n color: red;\n}'
res = parse(my_css)
self.assertEqual(res.text().strip(), reformatted_css)
section = cast(CssSection, res.sections[0])
self.assertFalse(section.declaration_block.declarations[0].semicolon)
def test_empty_block(self) -> None:
my_css = '''
div {
}'''
error = 'Empty declaration'
with self.assertRaisesRegex(CssParserException, error):
parse(my_css)
def test_multi_line_selector(self) -> None:
my_css = '''
h1,
h2,
h3 {
top: 0
}'''
res = parse(my_css)
section = res.sections[0]
selectors = section.selector_list.selectors
self.assertEqual(len(selectors), 3)
def test_media_block(self) -> None:
my_css = '''
@media (max-width: 300px) {
h5 {
margin: 0;
}
}'''
res = parse(my_css)
self.assertEqual(len(res.sections), 1)
expected = '@media (max-width: 300px) {\n h5 {\n margin: 0;\n }\n}'
self.assertEqual(res.text().strip(), expected)
class ParserTestSadPath(unittest.TestCase):
'''
Use this class for tests that verify the parser will
appropriately choke on malformed CSS.
We prevent some things that are technically legal
in CSS, like having comments in the middle of list
of selectors. Some of this is just for expediency;
some of this is to enforce consistent formatting.
'''
def _assert_error(self, my_css: str, error: str) -> None:
with self.assertRaisesRegex(CssParserException, error):
parse(my_css)
def test_unexpected_end_brace(self) -> None:
my_css = '''
@media (max-width: 975px) {
body {
color: red;
}
}} /* whoops */'''
error = 'unexpected }'
self._assert_error(my_css, error)
def test_empty_section(self) -> None:
my_css = '''
/* nothing to see here, move along */
'''
error = 'unexpected empty section'
self._assert_error(my_css, error)
def test_missing_colon(self) -> None:
my_css = '''
.hide
{
display none /* no colon here */
}'''
error = 'We expect a colon here'
self._assert_error(my_css, error)
def test_unclosed_comment(self) -> None:
my_css = ''' /* comment with no end'''
error = 'unclosed comment'
self._assert_error(my_css, error)
def test_missing_selectors(self) -> None:
my_css = '''
/* no selectors here */
{
bottom: 0
}'''
error = 'Missing selector'
self._assert_error(my_css, error)
def test_missing_value(self) -> None:
my_css = '''
h1
{
bottom:
}'''
error = 'Missing value'
self._assert_error(my_css, error)
def test_disallow_comments_in_selectors(self) -> None:
my_css = '''
h1,
h2, /* comment here not allowed by Zulip */
h3 {
top: 0
}'''
error = 'Comments in selector section are not allowed'
self._assert_error(my_css, error)