mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-08-17 00:20:42 +03:00
Tests n’ fixes. Lots of them.
This commit is contained in:
parent
ad3cdf5dae
commit
46b1d225c7
@ -1,5 +1,6 @@
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
def __repr__
|
||||
raise NotImplementedError
|
||||
omit =
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,8 @@
|
||||
*.pyc
|
||||
/*.egg-info
|
||||
/.coverage
|
||||
/htmlcov
|
||||
/coverage.xml
|
||||
/.tox
|
||||
/dist
|
||||
/env
|
||||
|
@ -73,5 +73,5 @@ def main(argv=None, stdout=sys.stdout, stdin=sys.stdin):
|
||||
getattr(html, 'write_' + format_)(output, stylesheets=args.stylesheet)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
main()
|
||||
|
@ -140,17 +140,6 @@ class StyleDict(object):
|
||||
object.__setattr__(style, 'anonymous', True)
|
||||
return style
|
||||
|
||||
def as_dict(self):
|
||||
"""Return a new dict completly separate from this object."""
|
||||
if hasattr(self._parent, 'as_dict'):
|
||||
rv = self._parent.as_dict()
|
||||
elif hasattr(self._parent, 'items'):
|
||||
rv = dict(self._parent)
|
||||
else:
|
||||
rv = {}
|
||||
rv.update(self._storage)
|
||||
return rv
|
||||
|
||||
def inherit_from(self):
|
||||
"""Return a new StyleDict with inherited properties from this one.
|
||||
|
||||
@ -167,7 +156,7 @@ class StyleDict(object):
|
||||
anonymous = False
|
||||
|
||||
|
||||
def find_stylesheets(document):
|
||||
def find_stylesheets(document, medium):
|
||||
"""Yield the stylesheets of ``document``.
|
||||
|
||||
The output order is the same as the order of the dom.
|
||||
@ -176,14 +165,15 @@ def find_stylesheets(document):
|
||||
for element in document.dom.iter():
|
||||
if element.tag not in ('style', 'link'):
|
||||
continue
|
||||
mimetype = element.get('type')
|
||||
mime_type = element.get('type', 'text/css').split(';', 1)[0].strip()
|
||||
# Only keep 'type/subtype' from 'type/subtype ; param1; param2'.
|
||||
if mimetype and mimetype.split(';', 1)[0].strip() != 'text/css':
|
||||
if mime_type != 'text/css':
|
||||
continue
|
||||
media_attr = element.get('media', '').strip() or 'all'
|
||||
media = [media_type.strip() for media_type in media_attr.split(',')]
|
||||
if not evaluate_media_query(media, medium):
|
||||
continue
|
||||
if element.tag == 'style':
|
||||
# TODO: handle the `scoped` attribute
|
||||
# Content is text that is directly in the <style> element, not its
|
||||
# descendants
|
||||
content = [element.text or '']
|
||||
@ -193,17 +183,14 @@ def find_stylesheets(document):
|
||||
# lxml should give us either unicode or ASCII-only bytestrings, so
|
||||
# we don't need `encoding` here.
|
||||
css = CSS(string=content, base_url=element.base_url)
|
||||
css.media = media
|
||||
yield css
|
||||
elif element.tag == 'link' and element.get('href') and (
|
||||
element.get('type', 'text/css').strip() == 'text/css'):
|
||||
elif element.tag == 'link' and element.get('href'):
|
||||
rel = element.get('rel', '').split()
|
||||
if 'stylesheet' not in rel or 'alternate' in rel:
|
||||
continue
|
||||
href = get_url_attribute(element, 'href')
|
||||
css = CSS(url=href, _check_mime_type=True)
|
||||
if css.mime_type == 'text/css':
|
||||
css.media = media
|
||||
yield css
|
||||
else:
|
||||
LOGGER.warn('Unsupported stylesheet type: %s', css.mime_type)
|
||||
@ -253,10 +240,9 @@ def declaration_precedence(origin, importance):
|
||||
return 3
|
||||
elif origin == 'author': # and importance
|
||||
return 4
|
||||
elif origin == 'user': # and importance
|
||||
return 5
|
||||
else:
|
||||
assert ValueError('Unkown origin: %r' % origin)
|
||||
assert origin == 'user' # and importance
|
||||
return 5
|
||||
|
||||
|
||||
def add_declaration(cascaded_styles, prop_name, prop_values, weight, element,
|
||||
@ -428,7 +414,7 @@ def get_all_computed_styles(document, medium,
|
||||
Return a dict of (DOM element, pseudo element type) -> StyleDict instance.
|
||||
|
||||
"""
|
||||
author_stylesheets = list(find_stylesheets(document))
|
||||
author_stylesheets = list(find_stylesheets(document, medium))
|
||||
|
||||
# keys: (element, pseudo_element_type)
|
||||
# element: a lxml element object or the '@page' string for @page styles
|
||||
|
@ -340,8 +340,6 @@ def compute_float(computer, name, value):
|
||||
@register_computer('font-size')
|
||||
def font_size(computer, name, value):
|
||||
"""Compute the ``font-size`` property."""
|
||||
if isinstance(value, (int, float)):
|
||||
return value
|
||||
if value in FONT_SIZE_KEYWORDS:
|
||||
return FONT_SIZE_KEYWORDS[value]
|
||||
|
||||
|
@ -21,8 +21,7 @@ from tinycss.parsing import split_on_comma
|
||||
from ..logger import LOGGER
|
||||
from ..formatting_structure import counters
|
||||
from ..compat import urljoin
|
||||
from .values import (get_keyword, get_single_keyword, as_css,
|
||||
make_percentage_value)
|
||||
from .values import get_keyword, get_single_keyword, make_percentage_value
|
||||
from .properties import INITIAL_VALUES, NOT_PRINT_MEDIA
|
||||
from . import computed_values
|
||||
|
||||
@ -157,6 +156,11 @@ def background_attachment(keyword):
|
||||
@validator('border-right-color')
|
||||
@validator('border-bottom-color')
|
||||
@validator('border-left-color')
|
||||
@single_value
|
||||
def other_colors(value):
|
||||
return parse_color(value)
|
||||
|
||||
|
||||
@validator('color')
|
||||
@single_value
|
||||
def color(value):
|
||||
@ -967,7 +971,7 @@ def expand_border_side(name, values):
|
||||
|
||||
"""
|
||||
for value in values:
|
||||
if color([value]) is not None:
|
||||
if parse_color(value) is not None:
|
||||
suffix = '_color'
|
||||
elif border_width([value]) is not None:
|
||||
suffix = '_width'
|
||||
@ -999,7 +1003,7 @@ def expand_background(name, values, base_url):
|
||||
values = list(reversed(values))
|
||||
while values:
|
||||
value = values.pop()
|
||||
if color([value]) is not None:
|
||||
if parse_color(value) is not None:
|
||||
suffix = '_color'
|
||||
elif image([value], base_url) is not None:
|
||||
suffix = '_image'
|
||||
|
@ -51,14 +51,11 @@ def get_percentage_value(value):
|
||||
return None
|
||||
|
||||
|
||||
def as_css(values):
|
||||
"""Return the string reperesentation of the ``values`` list."""
|
||||
return ' '.join('%s' % getattr(value, 'as_css', value) for value in values)
|
||||
class FakePercentage(object):
|
||||
type = 'PERCENTAGE'
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
self.as_css = '{}%'.format(value)
|
||||
|
||||
FakeValue = collections.namedtuple('FakeValue', ('type', 'value', 'as_css'))
|
||||
|
||||
|
||||
def make_percentage_value(value):
|
||||
"""Return an object that ``get_percentage_value()`` will accept."""
|
||||
return FakeValue('PERCENTAGE', value, '{0}%'.format(value))
|
||||
make_percentage_value = FakePercentage
|
||||
|
@ -144,15 +144,14 @@ def box_rectangle(box, which_rectangle):
|
||||
box.padding_width(),
|
||||
box.padding_height(),
|
||||
)
|
||||
elif which_rectangle == 'content-box':
|
||||
else:
|
||||
assert which_rectangle == 'content-box', which_rectangle
|
||||
return (
|
||||
box.content_box_x(),
|
||||
box.content_box_y(),
|
||||
box.width,
|
||||
box.height,
|
||||
)
|
||||
else:
|
||||
raise ValueError(which_rectangle)
|
||||
|
||||
|
||||
def background_positioning_area(page, box, style):
|
||||
|
@ -219,16 +219,14 @@ def update_counters(state, style):
|
||||
sibling_scopes.add(name)
|
||||
counter_values.setdefault(name, []).append(value)
|
||||
|
||||
# Disabled for now, only exists in Lists3’s editor’s draft.
|
||||
for name, value in []: # XXX style.counter_set:
|
||||
values = counter_values.setdefault(name, [])
|
||||
if not values:
|
||||
if name in sibling_scopes:
|
||||
counter_values[name].pop()
|
||||
else:
|
||||
sibling_scopes.add(name)
|
||||
values.append(0)
|
||||
values[-1] = value
|
||||
# XXX Disabled for now, only exists in Lists3’s editor’s draft.
|
||||
# for name, value in style.counter_set:
|
||||
# values = counter_values.setdefault(name, [])
|
||||
# if not values:
|
||||
# assert name not in sibling_scopes
|
||||
# sibling_scopes.add(name)
|
||||
# values.append(0)
|
||||
# values[-1] = value
|
||||
|
||||
counter_increment = style.counter_increment
|
||||
if counter_increment == 'auto':
|
||||
@ -243,10 +241,8 @@ def update_counters(state, style):
|
||||
for name, value in counter_increment:
|
||||
values = counter_values.setdefault(name, [])
|
||||
if not values:
|
||||
if name in sibling_scopes:
|
||||
counter_values[name].pop()
|
||||
else:
|
||||
sibling_scopes.add(name)
|
||||
assert name not in sibling_scopes
|
||||
sibling_scopes.add(name)
|
||||
values.append(0)
|
||||
values[-1] += value
|
||||
|
||||
|
@ -49,7 +49,7 @@ def block_level_layout(document, box, max_position_y, skip_stack,
|
||||
adjoining_margins = []
|
||||
collapsing_through = False
|
||||
return box, resume_at, next_page, adjoining_margins, collapsing_through
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
raise TypeError('Layout for %s not handled yet' % type(box).__name__)
|
||||
|
||||
|
||||
@ -373,7 +373,7 @@ def block_table_wrapper(document, wrapper, max_position_y, skip_stack,
|
||||
if isinstance(child, boxes.TableBox):
|
||||
table = child
|
||||
break
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
raise ValueError('Table wrapper without a table')
|
||||
resolve_percentages(wrapper, containing_block)
|
||||
resolve_percentages(table, containing_block)
|
||||
|
@ -265,7 +265,7 @@ def atomic_box(box, containing_block, device_size):
|
||||
image_marker_layout(box)
|
||||
else:
|
||||
inline_replaced_box_layout(box, containing_block, device_size)
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
raise TypeError('Layout for %s not handled yet' % type(box).__name__)
|
||||
return box
|
||||
|
||||
|
@ -351,12 +351,11 @@ def cell_baseline(cell):
|
||||
if child is None:
|
||||
stack.pop()
|
||||
continue
|
||||
if not child.is_in_normal_flow():
|
||||
continue
|
||||
if isinstance(child, (boxes.LineBox, boxes.TableRowBox)):
|
||||
# First in-flow line or row.
|
||||
return child.baseline + child.position_y - cell.position_y
|
||||
if isinstance(child, boxes.ParentBox):
|
||||
stack.append(iter(child.children))
|
||||
if child.is_in_normal_flow():
|
||||
if isinstance(child, (boxes.LineBox, boxes.TableRowBox)):
|
||||
# First in-flow line or row.
|
||||
return child.baseline + child.position_y - cell.position_y
|
||||
if isinstance(child, boxes.ParentBox):
|
||||
stack.append(iter(child.children))
|
||||
# Default to the bottom of the content area.
|
||||
return cell.border_top_width + cell.padding_top + cell.height
|
||||
|
@ -1,12 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv=Content-Type content=text/html;charset=utf8>
|
||||
<link rel=stylesheet href="sheet1.css" media=screen>
|
||||
<!-- currentColor means 'inherit' on color itself. -->
|
||||
<link rel=stylesheet href="data:text/css;charset=ASCII,a%7Bcolor%3AcurrentColor%7D">
|
||||
<style media=print>
|
||||
@import url(sheet1.css);
|
||||
@import url(data:text/css;charset=utf32;base64,//4AAGwAAABpAAAAewAAAGMAAABvAAAAbAAAAG8AAAByAAAAOgAAAHIAAABlAAAAZAAAAH0AAAA=);
|
||||
@import url(sub_directory/sheet1.css);
|
||||
@import "data:text/css;charset=utf-16-le;base64,\
|
||||
bABpAHsAYwBvAGwAbwByADoAcgBlAGQAfQA=";
|
||||
@import "data:,ul {border-width: 1000px !important}" screen;
|
||||
a:after {
|
||||
content: " [" attr(href) "]"
|
||||
}
|
||||
@ -14,6 +15,7 @@
|
||||
ul {
|
||||
border-style: none solid hidden;
|
||||
border-width: thin thick 4px .25in;
|
||||
font-weight: bold;
|
||||
}
|
||||
body > h1:first-child {
|
||||
background-image: url(logo_small.png);
|
||||
@ -22,12 +24,25 @@
|
||||
background: red;
|
||||
}
|
||||
</style>
|
||||
<style type=text/NOT-css>
|
||||
ul {
|
||||
border-width: 1000px !important;
|
||||
}
|
||||
</style>
|
||||
<style media=screen>
|
||||
ul {
|
||||
border-width: 1000px !important;
|
||||
}
|
||||
</style>
|
||||
<link rel=not_stylesheet href="data:,ul {border-width: 1000px !important}">
|
||||
</head>
|
||||
<body>
|
||||
<h1>WeasyPrint test document (with Ünicōde)</h1>
|
||||
<p style="color: black; font-size: 2em">Hello</p>
|
||||
<ul>
|
||||
<li><a href=home.html style="padding: 1px 2px 3px 4px">Home</a>
|
||||
<h1 style="font-size: 2em">WeasyPrint test document (with Ünicōde)</h1>
|
||||
<p style="color: blue; font-size: x-large">Hello</p>
|
||||
<ul style="font-size: 5ex">
|
||||
<li style="font-size: 6pt; font-weight: bolder">
|
||||
<a href=home.html
|
||||
style="padding: 1px 2px 3px 4px; font-size: 300%">Home</a>
|
||||
<li>…
|
||||
</ul>
|
||||
</body>
|
||||
|
@ -1,10 +0,0 @@
|
||||
@import url(sheet2.css) all;
|
||||
p {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
@media print {
|
||||
ul {
|
||||
margin: 2em;
|
||||
}
|
||||
}
|
16
weasyprint/tests/resources/sub_directory/sheet1.css
Normal file
16
weasyprint/tests/resources/sub_directory/sheet1.css
Normal file
@ -0,0 +1,16 @@
|
||||
@import url(../sheet2.css) all;
|
||||
p {
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
@media print {
|
||||
ul {
|
||||
/* Currently, 1ex == 0.5em exactly. */
|
||||
margin: 2em 4ex;
|
||||
}
|
||||
}
|
||||
@media screen {
|
||||
ul {
|
||||
border-width: 1000px !important;
|
||||
}
|
||||
}
|
@ -153,12 +153,11 @@ def sanity_checks(box):
|
||||
if not isinstance(box, boxes.ParentBox):
|
||||
return
|
||||
|
||||
acceptable_types_lists = None # raises when iterated
|
||||
for class_ in type(box).mro():
|
||||
if class_ in PROPER_CHILDREN:
|
||||
acceptable_types_lists = PROPER_CHILDREN[class_]
|
||||
break
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
assert any(
|
||||
all(isinstance(child, acceptable_types) for child in box.children)
|
||||
@ -218,6 +217,23 @@ def test_inline_in_block():
|
||||
box = build.inline_in_block(box)
|
||||
assert_tree(box, expected)
|
||||
|
||||
source = '<div><p>Lipsum.</p>Hello, <em>World</em>!\n</div>'
|
||||
expected = [
|
||||
('div', 'Block', [
|
||||
('p', 'Block', [
|
||||
('p', 'Line', [
|
||||
('p', 'Text', 'Lipsum.')])]),
|
||||
('div', 'AnonBlock', [
|
||||
('div', 'Line', [
|
||||
('div', 'Text', 'Hello, '),
|
||||
('em', 'Inline', [
|
||||
('em', 'Text', 'World')]),
|
||||
('div', 'Text', '!\n')])])])]
|
||||
|
||||
box = parse(source)
|
||||
box = build.inline_in_block(box)
|
||||
assert_tree(box, expected)
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_block_in_inline():
|
||||
|
@ -63,6 +63,9 @@ def test_data_url():
|
||||
parse('data:text/plain;base64,Zm9vbw', b'fooo', 'text/plain', None)
|
||||
parse('data:text/plain;base64,Zm9vb28', b'foooo', 'text/plain', None)
|
||||
|
||||
with raises(IOError):
|
||||
parse_data_url('data:foo')
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_style_dict():
|
||||
@ -81,22 +84,20 @@ def test_find_stylesheets():
|
||||
"""Test if the stylesheets are found in a HTML document."""
|
||||
document = parse_html('doc1.html')
|
||||
|
||||
sheets = list(css.find_stylesheets(document))
|
||||
assert len(sheets) == 3
|
||||
sheets = list(css.find_stylesheets(document, 'print'))
|
||||
assert len(sheets) == 2
|
||||
# Also test that stylesheets are in tree order
|
||||
assert [s.base_url.rsplit('/', 1)[-1].rsplit(',', 1)[-1] for s in sheets] \
|
||||
== ['sheet1.css', 'a%7Bcolor%3AcurrentColor%7D',
|
||||
'doc1.html']
|
||||
== ['a%7Bcolor%3AcurrentColor%7D', 'doc1.html']
|
||||
|
||||
rules = [rule for sheet in sheets for rule in sheet.rules]
|
||||
assert len(rules) == 13
|
||||
assert len(rules) == 10
|
||||
# Also test appearance order
|
||||
assert [
|
||||
rule.selector if rule.at_keyword else ''.join(
|
||||
v.as_css for v in rule.selector)
|
||||
for rule, _selector_list, _declarations in rules
|
||||
] == [
|
||||
'li', 'p', 'ul', # imported
|
||||
'a', 'li', 'p', 'ul', 'li', 'a:after', (None, 'first'), 'ul',
|
||||
'body > h1:first-child', 'h1 ~ p ~ ul a:after'
|
||||
]
|
||||
@ -117,7 +118,7 @@ def test_expand_shorthands():
|
||||
assert 'margin-top' not in style
|
||||
|
||||
style = dict(
|
||||
(name, css.values.as_css([value]))
|
||||
(name, value.as_css)
|
||||
for _rule, _selectors, declarations in sheet.rules
|
||||
for name, value, _priority in declarations)
|
||||
|
||||
@ -159,39 +160,43 @@ def test_annotate_document():
|
||||
+ os.path.abspath(resource_filename('logo_small.png'))
|
||||
|
||||
assert h1.font_weight == 700
|
||||
assert h1.font_size == 32 # 4ex
|
||||
|
||||
# 32px = 1em * font-size: 2em * initial 16px
|
||||
assert p.margin_top == 32
|
||||
# 32px = 1em * font-size = x-large = 3/2 * initial 16px = 24px
|
||||
assert p.margin_top == 24
|
||||
assert p.margin_right == 0
|
||||
assert p.margin_bottom == 32
|
||||
assert p.margin_bottom == 24
|
||||
assert p.margin_left == 0
|
||||
assert p.background_color == (0, 0, 1, 1) # blue
|
||||
|
||||
# 32px = 2em * initial 16px
|
||||
assert ul.margin_top == 32
|
||||
assert ul.margin_right == 32
|
||||
assert ul.margin_bottom == 32
|
||||
assert ul.margin_left == 32
|
||||
# 80px = 2em * 5ex = 10 * half of initial 16px
|
||||
assert ul.margin_top == 80
|
||||
assert ul.margin_right == 80
|
||||
assert ul.margin_bottom == 80
|
||||
assert ul.margin_left == 80
|
||||
|
||||
assert ul.font_weight == 700
|
||||
# thick = 5px, 0.25 inches = 96*.25 = 24px
|
||||
assert ul.border_top_width == 0
|
||||
assert ul.border_right_width == 5
|
||||
assert ul.border_bottom_width == 0
|
||||
assert ul.border_left_width == 24
|
||||
|
||||
# 32px = 2em * initial 16px
|
||||
# 64px = 4em * initial 16px
|
||||
assert li_0.margin_top == 32
|
||||
assert li_0.font_weight == 900
|
||||
assert li_0.font_size == 8 # 6pt
|
||||
assert li_0.margin_top == 16 # 2em
|
||||
assert li_0.margin_right == 0
|
||||
assert li_0.margin_bottom == 32
|
||||
assert li_0.margin_left == 64
|
||||
assert li_0.margin_bottom == 16
|
||||
assert li_0.margin_left == 32 # 4em
|
||||
|
||||
assert a.text_decoration == frozenset(['underline'])
|
||||
|
||||
assert a.font_size == 24 # 300% of 8px
|
||||
assert a.padding_top == 1
|
||||
assert a.padding_right == 2
|
||||
assert a.padding_bottom == 3
|
||||
assert a.padding_left == 4
|
||||
|
||||
|
||||
assert a.color == (1, 0, 0, 1)
|
||||
# Test the initial border-color: currentColor
|
||||
assert a.border_top_color == (1, 0, 0, 1)
|
||||
@ -343,4 +348,39 @@ def test_line_height_inheritance():
|
||||
assert paragraph.style.font_size == 20
|
||||
# 1.4 is inherited from p, 1.4 * 20px on em = 28px
|
||||
assert used_line_height(paragraph.style) == 28
|
||||
assert paragraph.style.vertical_align == 14 # 50% of 28pxhh
|
||||
assert paragraph.style.vertical_align == 14 # 50% of 28px
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_important():
|
||||
document = TestPNGDocument('''
|
||||
<style>
|
||||
p:nth-child(1) { color: lime }
|
||||
body p:nth-child(2) { color: red }
|
||||
|
||||
p:nth-child(3) { color: lime !important }
|
||||
body p:nth-child(3) { color: red }
|
||||
|
||||
body p:nth-child(5) { color: lime }
|
||||
p:nth-child(5) { color: red }
|
||||
|
||||
p:nth-child(6) { color: red }
|
||||
p:nth-child(6) { color: lime }
|
||||
</style>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
''', user_stylesheets=[CSS(string='''
|
||||
body p:nth-child(1) { color: red }
|
||||
p:nth-child(2) { color: lime !important }
|
||||
|
||||
p:nth-child(4) { color: lime !important }
|
||||
body p:nth-child(4) { color: red }
|
||||
''')])
|
||||
html = document.formatting_structure
|
||||
body, = html.children
|
||||
for paragraph in body.children:
|
||||
assert paragraph.style.color == (0, 1, 0, 1) # lime (light green)
|
||||
|
@ -14,25 +14,25 @@ from __future__ import division, unicode_literals
|
||||
|
||||
from pytest import raises
|
||||
from tinycss.color3 import RGBA
|
||||
from tinycss.parsing import remove_whitespace
|
||||
|
||||
from .testing_utils import assert_no_logs
|
||||
from ..css import validation, PARSER
|
||||
from ..css.values import as_css
|
||||
|
||||
# TODO: merge this into test_css.py ?
|
||||
|
||||
|
||||
# TODO: rewrite this mess
|
||||
def expand_to_dict(short_name, short_values):
|
||||
"""Helper to test shorthand properties expander functions."""
|
||||
return dict((name, value if isinstance(value, RGBA)
|
||||
else as_css(value) if isinstance(value, (list, tuple))
|
||||
declarations, errors = PARSER.parse_style_attr('prop: ' + short_values)
|
||||
assert not errors
|
||||
assert len(declarations) == 1
|
||||
tokens = remove_whitespace(declarations[0].value)
|
||||
expanded = validation.EXPANDERS[short_name]('', short_name, tokens)
|
||||
return dict((name, tuple(v.as_css for v in value)
|
||||
if name == 'background_position'
|
||||
else getattr(value, 'as_css', value))
|
||||
for name, value in validation.EXPANDERS[short_name](
|
||||
'', short_name,
|
||||
[token for token in PARSER.parse_style_attr(
|
||||
'prop: ' + short_values)[0][0].value
|
||||
if token.type != 'S']))
|
||||
for name, value in expanded)
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
@ -162,7 +162,7 @@ def test_expand_background():
|
||||
image='none',
|
||||
repeat='repeat',
|
||||
attachment='scroll',
|
||||
position='0% 0%'
|
||||
position=('0%', '0%'),
|
||||
|
||||
)
|
||||
assert_background(
|
||||
@ -171,7 +171,7 @@ def test_expand_background():
|
||||
image='foo.png', ##
|
||||
repeat='repeat',
|
||||
attachment='scroll',
|
||||
position='0% 0%'
|
||||
position=('0%', '0%'),
|
||||
)
|
||||
assert_background(
|
||||
'no-repeat',
|
||||
@ -179,7 +179,7 @@ def test_expand_background():
|
||||
image='none',
|
||||
repeat='no-repeat', ##
|
||||
attachment='scroll',
|
||||
position='0% 0%'
|
||||
position=('0%', '0%'),
|
||||
)
|
||||
assert_background(
|
||||
'fixed',
|
||||
@ -187,7 +187,7 @@ def test_expand_background():
|
||||
image='none',
|
||||
repeat='repeat',
|
||||
attachment='fixed', ##
|
||||
position='0% 0%'
|
||||
position=('0%', '0%'),
|
||||
)
|
||||
assert_background(
|
||||
'top right',
|
||||
@ -196,7 +196,7 @@ def test_expand_background():
|
||||
repeat='repeat',
|
||||
attachment='scroll',
|
||||
# Order swapped to be in (horizontal, vertical) order.
|
||||
position='100% 0%' ##
|
||||
position=('100%', '0%'), ##
|
||||
)
|
||||
assert_background(
|
||||
'url(bar) #f00 repeat-y center left fixed',
|
||||
@ -205,7 +205,7 @@ def test_expand_background():
|
||||
repeat='repeat-y', ##
|
||||
attachment='fixed', ##
|
||||
# Order swapped to be in (horizontal, vertical) order.
|
||||
position='0% 50%' ##
|
||||
position=('0%', '50%'), ##
|
||||
)
|
||||
assert_background(
|
||||
'#00f 10% 200px',
|
||||
@ -213,7 +213,7 @@ def test_expand_background():
|
||||
image='none',
|
||||
repeat='repeat',
|
||||
attachment='scroll',
|
||||
position='10% 200px' ##
|
||||
position=('10%', '200px'), ##
|
||||
)
|
||||
assert_background(
|
||||
'right 78px fixed',
|
||||
@ -221,20 +221,20 @@ def test_expand_background():
|
||||
image='none',
|
||||
repeat='repeat',
|
||||
attachment='fixed', ##
|
||||
position='100% 78px' ##
|
||||
position=('100%', '78px'), ##
|
||||
)
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_font():
|
||||
"""Test the ``font`` property."""
|
||||
assert expand_to_dict('font', '12px sans_serif') == {
|
||||
assert expand_to_dict('font', '12px My Fancy Font, serif') == {
|
||||
'font_style': 'normal',
|
||||
'font_variant': 'normal',
|
||||
'font_weight': 400,
|
||||
'font_size': '12px', ##
|
||||
'line_height': 'normal',
|
||||
'font_family': 'sans_serif', ##
|
||||
'font_family': ['My Fancy Font', 'serif'], ##
|
||||
}
|
||||
assert expand_to_dict('font', 'small/1.2 "Some Font", serif') == {
|
||||
'font_style': 'normal',
|
||||
@ -242,8 +242,7 @@ def test_font():
|
||||
'font_weight': 400,
|
||||
'font_size': 'small', ##
|
||||
'line_height': '1.2', ##
|
||||
# The comma and quotes were lost in expand_to_dict()
|
||||
'font_family': 'Some Font serif', ##
|
||||
'font_family': ['Some Font', 'serif'], ##
|
||||
}
|
||||
assert expand_to_dict('font', 'small-caps italic 700 large serif') == {
|
||||
'font_style': 'italic', ##
|
||||
@ -251,5 +250,5 @@ def test_font():
|
||||
'font_weight': 700, ##
|
||||
'font_size': 'large', ##
|
||||
'line_height': 'normal',
|
||||
'font_family': 'serif', ##
|
||||
'font_family': ['serif'], ##
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ BYTES_PER_PIXELS = 4
|
||||
PIXEL_FORMAT = 'rgba(%i, %i, %i, %i)'
|
||||
|
||||
|
||||
def format_pixel(pixels, width, x, y):
|
||||
def format_pixel(pixels, width, x, y): # pragma: no cover
|
||||
"""Return the pixel color as ``#RRGGBB``."""
|
||||
start = (y * width + x) * BYTES_PER_PIXELS
|
||||
end = start + BYTES_PER_PIXELS
|
||||
@ -93,13 +93,13 @@ def assert_different_renderings(expected_width, expected_height, documents):
|
||||
|
||||
for i, (name_1, lines_1) in enumerate(lines_list):
|
||||
for name_2, lines_2 in lines_list[i + 1:]:
|
||||
if lines_1 == lines_2:
|
||||
if lines_1 == lines_2: # pragma: no cover
|
||||
# Same as "assert lines_1 != lines_2" but the output of
|
||||
# the assert hook would be gigantic and useless.
|
||||
assert False, '%s and %s are the same' % (name_1, name_2)
|
||||
|
||||
|
||||
def write_png(basename, lines, width, height):
|
||||
def write_png(basename, lines, width, height): # pragma: no cover
|
||||
"""Take a pixel matrix and write a PNG file."""
|
||||
directory = os.path.join(os.path.dirname(__file__), 'test_results')
|
||||
if not os.path.isdir(directory):
|
||||
@ -144,7 +144,7 @@ def assert_pixels_equal(name, width, height, lines, expected_lines):
|
||||
Take 2 matrices of height by width pixels and assert that they
|
||||
are the same.
|
||||
"""
|
||||
if lines != expected_lines:
|
||||
if lines != expected_lines: # pragma: no cover
|
||||
write_png(name + '.expected', expected_lines, width, height)
|
||||
write_png(name, lines, width, height)
|
||||
for y in xrange(height):
|
||||
|
@ -372,17 +372,21 @@ def test_lists():
|
||||
@assert_no_logs
|
||||
def test_empty_linebox():
|
||||
"""Test lineboxes with no content other than space-like characters."""
|
||||
page, = parse('''
|
||||
<style>
|
||||
p { font-size: 12px; width: 500px;
|
||||
font-family:%(fonts)s;}
|
||||
</style>
|
||||
<p> </p>
|
||||
''' % {'fonts': FONTS})
|
||||
page, = parse('<p> </p>')
|
||||
paragraph, = body_children(page)
|
||||
assert len(paragraph.children) == 0
|
||||
assert paragraph.height == 0
|
||||
|
||||
# Whitespace removed at the beginning of the line => empty line => no line
|
||||
page, = parse('''
|
||||
<style>
|
||||
p { width: 1px }
|
||||
</style>
|
||||
<p><br> </p>
|
||||
''')
|
||||
paragraph, = body_children(page)
|
||||
assert len(paragraph.children) == 1
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_breaking_linebox():
|
||||
@ -1421,6 +1425,20 @@ def test_text_align_justify():
|
||||
|
||||
assert image_5.position_x == 0
|
||||
|
||||
# single-word line (zero spaces)
|
||||
page, = parse('''
|
||||
<style>
|
||||
body { text-align: justify; width: 50px }
|
||||
</style>
|
||||
<p>Supercalifragilisticexpialidocious bar</p>
|
||||
''')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
paragraph, = body.children
|
||||
line_1, line_2 = paragraph.children
|
||||
text, = line_1.children
|
||||
assert text.position_x == 0
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_word_spacing():
|
||||
@ -1454,23 +1472,23 @@ def test_word_spacing():
|
||||
def test_letter_spacing():
|
||||
"""Test letter-spacing."""
|
||||
page, = parse('''
|
||||
<body><strong>Supercalifragilisticexpialidocious></strong>
|
||||
<body><strong>Supercalifragilisticexpialidocious</strong>
|
||||
''')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
line, = body.children
|
||||
strong_1, = line.children
|
||||
assert 280 <= strong_1.width <= 310
|
||||
assert 250 <= strong_1.width <= 300
|
||||
|
||||
page, = parse('''
|
||||
<style>strong { letter-spacing: 11px }</style>
|
||||
<body><strong>Supercalifragilisticexpialidocious></strong>
|
||||
<body><strong>Supercalifragilisticexpialidocious</strong>
|
||||
''')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
line, = body.children
|
||||
strong_2, = line.children
|
||||
assert strong_2.width - strong_1.width == 34 * 11
|
||||
assert strong_2.width - strong_1.width == 33 * 11
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
|
@ -38,10 +38,10 @@ class TestPNGDocument(PNGDocument):
|
||||
This stylesheet is shorter, which makes tests faster.
|
||||
|
||||
"""
|
||||
def __init__(self, html_source, base_url=None):
|
||||
def __init__(self, html_source, base_url=None, user_stylesheets=()):
|
||||
super(TestPNGDocument, self).__init__(
|
||||
HTML(string=html_source, base_url=base_url).root_element,
|
||||
user_stylesheets=[],
|
||||
user_stylesheets=user_stylesheets,
|
||||
user_agent_stylesheets=[TEST_UA_STYLESHEET])
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@ def assert_no_logs(function):
|
||||
with capture_logs() as logs:
|
||||
try:
|
||||
function()
|
||||
except Exception:
|
||||
except Exception: # pragma: no cover
|
||||
if logs:
|
||||
print('%i errors logged:' % len(logs), file=sys.stderr)
|
||||
raise
|
||||
|
Loading…
Reference in New Issue
Block a user