mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			167 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			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)
 |