frappe/env/lib/python3.12/site-packages/cssutils/tests/test_parse.py

558 lines
19 KiB
Python
Raw Normal View History

"""Tests for parsing which does not raise Exceptions normally"""
import xml.dom
import cssutils
import urllib.request
import urllib.error
import urllib.parse
from unittest import mock
import pytest
class TestCSSParser:
def _make_fetcher(self, encoding, content):
"make an URL fetcher with specified data"
def fetcher(url):
return encoding, content
return fetcher
def test_init(self):
"CSSParser.__init__()"
assert cssutils.log.raiseExceptions
# also the default:
cssutils.log.raiseExceptions = True
# default non raising parser
p = cssutils.CSSParser()
s = p.parseString('$')
assert s.cssText == b''
# explicit raiseExceptions=False
p = cssutils.CSSParser(raiseExceptions=False)
s = p.parseString('$')
assert s.cssText == b''
# working with sheet does raise though!
with pytest.raises(xml.dom.DOMException):
s.__setattr__('cssText', '$')
# ----
# raiseExceptions=True
p = cssutils.CSSParser(raiseExceptions=True)
with pytest.raises(xml.dom.SyntaxErr):
p.parseString('$')
# working with a sheet does raise too
s = cssutils.css.CSSStyleSheet()
with pytest.raises(xml.dom.DOMException):
s.__setattr__('cssText', '$')
# RESET cssutils.log.raiseExceptions
cssutils.log.raiseExceptions = False
s = cssutils.css.CSSStyleSheet()
# does not raise!
s.__setattr__('cssText', '$')
assert s.cssText == b''
def test_parseComments(self):
"cssutils.CSSParser(parseComments=False)"
css = '/*1*/ a { color: /*2*/ red; }'
p = cssutils.CSSParser(parseComments=False)
assert p.parseString(css).cssText == b'a {\n color: red\n }'
p = cssutils.CSSParser(parseComments=True)
assert p.parseString(css).cssText == b'/*1*/\na {\n color: /*2*/ red\n }'
def test_parseUrl(self):
"CSSParser.parseUrl()"
# parseUrl(self, href, encoding=None, media=None, title=None):
parser = cssutils.CSSParser()
m = mock.Mock()
with mock.patch('cssutils.util._defaultFetcher', m):
m.return_value = (None, '')
sheet = parser.parseUrl(
'http://example.com', media='tv,print', title='test'
)
assert sheet.href == 'http://example.com'
assert sheet.encoding == 'utf-8'
assert sheet.media.mediaText == 'tv, print'
assert sheet.title == 'test'
# URL and content tests
tests = {
# (url, content): isSheet, encoding, cssText
('', None): (False, None, None),
('1', None): (False, None, None),
('mailto:a@bb.cd', None): (False, None, None),
('http://cthedot.de/test.css', None): (False, None, None),
('http://cthedot.de/test.css', ''): (True, 'utf-8', ''),
('http://cthedot.de/test.css', 'a'): (True, 'utf-8', ''),
('http://cthedot.de/test.css', 'a {color: red}'): (
True,
'utf-8',
'a {\n color: red\n }',
),
('http://cthedot.de/test.css', '@charset "ascii";a {color: red}'): (
True,
'ascii',
'@charset "ascii";\na {\n color: red\n }',
),
}
override = 'iso-8859-1'
overrideprefix = '@charset "iso-8859-1";'
httpencoding = None
for (url, content), (isSheet, expencoding, cssText) in list(tests.items()):
parser.setFetcher(self._make_fetcher(httpencoding, content))
sheet1 = parser.parseUrl(url)
sheet2 = parser.parseUrl(url, encoding=override)
if isSheet:
assert sheet1.encoding == expencoding
assert sheet1.cssText == cssText.encode()
assert sheet2.encoding == override
if sheet1.cssText and cssText.startswith('@charset'):
assert sheet2.cssText == (
cssText.replace('ascii', override).encode()
)
elif sheet1.cssText:
assert sheet2.cssText == (overrideprefix + '\n' + cssText).encode()
else:
assert sheet2.cssText == (overrideprefix + cssText).encode()
else:
assert sheet1 is None
assert sheet2 is None
parser.setFetcher(None)
with pytest.raises(ValueError):
parser.parseUrl('../not-valid-in-urllib')
with pytest.raises(urllib.error.HTTPError):
parser.parseUrl(
'http://cthedot.de/not-present.css',
)
def test_parseString(self):
"CSSParser.parseString()"
tests = {
# (byte) string, encoding: encoding, cssText
('/*a*/', None): ('utf-8', b'/*a*/'),
('/*a*/', 'ascii'): ('ascii', b'@charset "ascii";\n/*a*/'),
# org
# ('/*\xc3\xa4*/', None): (u'utf-8', u'/*\xc3\xa4*/'.encode('utf-8')),
# ('/*\xc3\xa4*/', 'utf-8'): (u'utf-8',
# u'@charset "utf-8";\n/*\xc3\xa4*/'.encode('utf-8')),
# new for 2.x and 3.x
('/*\xe4*/'.encode(), None): ('utf-8', '/*\xe4*/'.encode()),
('/*\xe4*/'.encode(), 'utf-8'): (
'utf-8',
'@charset "utf-8";\n/*\xe4*/'.encode(),
),
('@charset "ascii";/*a*/', None): (
'ascii',
b'@charset "ascii";\n/*a*/',
),
('@charset "utf-8";/*a*/', None): (
'utf-8',
b'@charset "utf-8";\n/*a*/',
),
('@charset "iso-8859-1";/*a*/', None): (
'iso-8859-1',
b'@charset "iso-8859-1";\n/*a*/',
),
# unicode string, no encoding: encoding, cssText
('/*€*/', None): ('utf-8', '/*€*/'.encode()),
('@charset "iso-8859-1";/*ä*/', None): (
'iso-8859-1',
'@charset "iso-8859-1";\n/*ä*/'.encode('iso-8859-1'),
),
('@charset "utf-8";/*€*/', None): (
'utf-8',
'@charset "utf-8";\n/*€*/'.encode(),
),
('@charset "utf-16";/**/', None): (
'utf-16',
'@charset "utf-16";\n/**/'.encode('utf-16'),
),
# unicode string, encoding utf-8: encoding, cssText
('/*€*/', 'utf-8'): ('utf-8', '@charset "utf-8";\n/*€*/'.encode()),
('@charset "iso-8859-1";/*ä*/', 'utf-8'): (
'utf-8',
'@charset "utf-8";\n/*ä*/'.encode(),
),
('@charset "utf-8";/*€*/', 'utf-8'): (
'utf-8',
'@charset "utf-8";\n/*€*/'.encode(),
),
('@charset "utf-16";/**/', 'utf-8'): (
'utf-8',
b'@charset "utf-8";\n/**/',
),
# probably not what is wanted but does not raise:
('/*€*/', 'ascii'): (
'ascii',
b'@charset "ascii";\n/*\\20AC */',
),
('/*€*/', 'iso-8859-1'): (
'iso-8859-1',
b'@charset "iso-8859-1";\n/*\\20AC */',
),
}
for test in tests:
css, encoding = test
sheet = cssutils.parseString(css, encoding=encoding)
encoding, cssText = tests[test]
assert encoding == sheet.encoding
assert cssText == sheet.cssText
tests = [
# encoded css, overiding encoding
('/*€*/'.encode('utf-16'), 'utf-8'),
('/*ä*/'.encode('iso-8859-1'), 'ascii'),
('/*€*/'.encode(), 'ascii'),
(b'a', 'utf-16'),
]
for test in tests:
# self.assertEqual(None, cssutils.parseString(css, encoding=encoding))
with pytest.raises(UnicodeDecodeError):
cssutils.parseString(test[0], test[1])
def test_validate(self):
"""CSSParser(validate)"""
style = 'color: red'
t = 'a { %s }' % style
# helper
s = cssutils.parseString(t)
assert s.validating
s = cssutils.parseString(t, validate=False)
assert s.validating is False
s = cssutils.parseString(t, validate=True)
assert s.validating
d = cssutils.parseStyle(style)
assert d.validating
d = cssutils.parseStyle(style, validate=True)
assert d.validating
d = cssutils.parseStyle(style, validate=False)
assert d.validating is False
# parser
p = cssutils.CSSParser()
s = p.parseString(t)
assert s.validating
s = p.parseString(t, validate=False)
assert s.validating is False
s = p.parseString(t, validate=True)
assert s.validating
d = p.parseStyle(style)
assert d.validating
p = cssutils.CSSParser(validate=True)
s = p.parseString(t)
assert s.validating
s = p.parseString(t, validate=False)
assert s.validating is False
s = p.parseString(t, validate=True)
assert s.validating
d = p.parseStyle(style)
assert d.validating
p = cssutils.CSSParser(validate=False)
s = p.parseString(t)
assert s.validating is False
s = p.parseString(t, validate=False)
assert s.validating is False
s = p.parseString(t, validate=True)
assert s.validating
d = p.parseStyle(style)
assert d.validating is False
# url
p = cssutils.CSSParser(validate=False)
p.setFetcher(self._make_fetcher('utf-8', t))
u = 'url'
s = p.parseUrl(u)
assert s.validating is False
s = p.parseUrl(u, validate=False)
assert s.validating is False
s = p.parseUrl(u, validate=True)
assert s.validating
# check if it raises see log test
def test_fetcher(self):
"""CSSParser.fetcher
order:
0. explicity given encoding OVERRIDE (cssutils only)
1. An HTTP "charset" parameter in a "Content-Type" field
(or similar parameters in other protocols)
2. BOM and/or @charset (see below)
3. <link charset=""> or other metadata from the linking mechanism (if any)
4. charset of referring style sheet or document (if any)
5. Assume UTF-8
"""
tests = {
# css, encoding, (mimetype, encoding, importcss):
# encoding, importIndex, importEncoding, importText
# 0/0 override/override => ASCII/ASCII
(
'@charset "utf-16"; @import "x";',
'ASCII',
('iso-8859-1', '@charset "latin1";/*t*/'),
): ('ascii', 1, 'ascii', b'@charset "ascii";\n/*t*/'),
# 1/1 not tested her but same as next
# 2/1 @charset/HTTP => UTF-16/ISO-8859-1
(
'@charset "UTF-16"; @import "x";',
None,
('ISO-8859-1', '@charset "latin1";/*t*/'),
): (
'utf-16',
1,
'iso-8859-1',
b'@charset "iso-8859-1";\n/*t*/',
),
# 2/2 @charset/@charset => UTF-16/ISO-8859-1
(
'@charset "UTF-16"; @import "x";',
None,
(None, '@charset "ISO-8859-1";/*t*/'),
): (
'utf-16',
1,
'iso-8859-1',
b'@charset "iso-8859-1";\n/*t*/',
),
# 2/4 @charset/referrer => ASCII/ASCII
('@charset "ASCII"; @import "x";', None, (None, '/*t*/')): (
'ascii',
1,
'ascii',
b'@charset "ascii";\n/*t*/',
),
# 5/5 default/default or referrer
('@import "x";', None, (None, '/*t*/')): (
'utf-8',
0,
'utf-8',
b'/*t*/',
),
# 0/0 override/override+unicode
(
'@charset "utf-16"; @import "x";',
'ASCII',
(None, '@charset "latin1";/*\u0287*/'),
): ('ascii', 1, 'ascii', b'@charset "ascii";\n/*\\287 */'),
# 2/1 @charset/HTTP+unicode
('@charset "ascii"; @import "x";', None, ('iso-8859-1', '/*\u0287*/')): (
'ascii',
1,
'iso-8859-1',
b'@charset "iso-8859-1";\n/*\\287 */',
),
# 2/4 @charset/referrer+unicode
('@charset "ascii"; @import "x";', None, (None, '/*\u0287*/')): (
'ascii',
1,
'ascii',
b'@charset "ascii";\n/*\\287 */',
),
# 5/1 default/HTTP+unicode
('@import "x";', None, ('ascii', '/*\u0287*/')): (
'utf-8',
0,
'ascii',
b'@charset "ascii";\n/*\\287 */',
),
# 5/5 default+unicode/default+unicode
('@import "x";', None, (None, '/*\u0287*/')): (
'utf-8',
0,
'utf-8',
'/*\u0287*/'.encode(),
),
}
parser = cssutils.CSSParser()
for test in tests:
css, encoding, fetchdata = test
sheetencoding, importIndex, importEncoding, importText = tests[test]
# use setFetcher
parser.setFetcher(self._make_fetcher(*fetchdata))
# use init
parser2 = cssutils.CSSParser(fetcher=self._make_fetcher(*fetchdata))
sheet = parser.parseString(css, encoding=encoding)
sheet2 = parser2.parseString(css, encoding=encoding)
# sheet
assert sheet.encoding == sheetencoding
assert sheet2.encoding == sheetencoding
# imported sheet
assert sheet.cssRules[importIndex].styleSheet.encoding == importEncoding
assert sheet2.cssRules[importIndex].styleSheet.encoding == importEncoding
assert sheet.cssRules[importIndex].styleSheet.cssText == importText
assert sheet2.cssRules[importIndex].styleSheet.cssText == importText
def test_roundtrip(self):
"cssutils encodings"
css1 = r'''@charset "utf-8";
/* ä */'''
s = cssutils.parseString(css1)
css2 = str(s.cssText, 'utf-8')
assert css1 == css2
s = cssutils.parseString(css2)
s.cssRules[0].encoding = 'ascii'
css3 = r'''@charset "ascii";
/* \E4 */'''
assert css3 == str(s.cssText, 'utf-8')
def test_escapes(self):
"cssutils escapes"
css = r'\43\x { \43\x: \43\x !import\41nt }'
sheet = cssutils.parseString(css)
assert (
sheet.cssText
== r'''C\x {
c\x: C\x !important
}'''.encode()
)
css = r'\ x{\ x :\ x ;y:1} '
sheet = cssutils.parseString(css)
assert (
sheet.cssText
== br'''\ x {
\ x: \ x;
y: 1
}'''
)
def test_invalidstring(self):
"cssutils.parseString(INVALID_STRING)"
validfromhere = '@namespace "x";'
csss = (
'''@charset "ascii
;'''
+ validfromhere,
'''@charset 'ascii
;'''
+ validfromhere,
'''@namespace "y
;'''
+ validfromhere,
'''@import "y
;'''
+ validfromhere,
'''@import url('a
);'''
+ validfromhere,
'''@unknown "y
;'''
+ validfromhere,
)
for css in csss:
s = cssutils.parseString(css)
assert validfromhere.encode() == s.cssText
csss = (
'''a { font-family: "Courier
; }''',
r'''a { content: "\"; }
''',
r'''a { content: "\\\"; }
''',
)
for css in csss:
assert b'' == cssutils.parseString(css).cssText
def test_invalid(self):
"cssutils.parseString(INVALID_CSS)"
tests = {
'a {color: blue}} a{color: red} a{color: green}': '''a {
color: blue
}
a {
color: green
}''',
'p @here {color: red} p {color: green}': 'p {\n color: green\n }',
}
for css in tests:
exp = tests[css]
if exp is None:
exp = css
s = cssutils.parseString(css)
assert exp.encode() == s.cssText
def test_nesting(self):
"cssutils.parseString nesting"
# examples from csslist 27.11.2007
tests = {
'@1; div{color:green}': 'div {\n color: green\n }',
'@1 []; div{color:green}': 'div {\n color: green\n }',
'@1 [{}]; div { color:green; }': 'div {\n color: green\n }',
'@media all { @ } div{color:green}': 'div {\n color: green\n }',
# should this be u''?
'@1 { [ } div{color:green}': '',
# red was eaten:
'@1 { [ } ] div{color:red}div{color:green}': 'div {\n color: green\n }',
}
for css, exp in list(tests.items()):
assert exp.encode() == cssutils.parseString(css).cssText
def test_specialcases(self):
"cssutils.parseString(special_case)"
tests = {
'''
a[title="a not s\
o very long title"] {/*...*/}''': '''a[title="a not so very long title"] {
/*...*/
}'''
}
for css in tests:
exp = tests[css]
if exp is None:
exp = css
s = cssutils.parseString(css)
assert exp.encode() == s.cssText
def test_iehack(self):
"IEhack: $property (not since 0.9.5b3)"
# $color is not color!
css = 'a { color: green; $color: red; }'
s = cssutils.parseString(css)
p1 = s.cssRules[0].style.getProperty('color')
assert 'color' == p1.name
assert 'color' == p1.literalname
assert '' == s.cssRules[0].style.getPropertyValue('$color')
p2 = s.cssRules[0].style.getProperty('$color')
assert p2 is None
assert 'green' == s.cssRules[0].style.getPropertyValue('color')
assert 'green' == s.cssRules[0].style.color
def test_attributes(self):
"cssutils.parseString(href, media)"
s = cssutils.parseString(
"a{}", href="file:foo.css", media="screen, projection, tv"
)
assert s.href == "file:foo.css"
assert s.media.mediaText == "screen, projection, tv"
s = cssutils.parseString(
"a{}", href="file:foo.css", media=["screen", "projection", "tv"]
)
assert s.media.mediaText == "screen, projection, tv"