mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-05 08:27:22 +03:00
1121 lines
39 KiB
Python
1121 lines
39 KiB
Python
"""
|
|
weasyprint.tests.test_css_properties
|
|
------------------------------------
|
|
|
|
Test expanders for shorthand properties.
|
|
|
|
"""
|
|
|
|
import math
|
|
|
|
import pytest
|
|
import tinycss2
|
|
|
|
from ..css import preprocess_declarations
|
|
from ..css.computed_values import ZERO_PIXELS
|
|
from ..css.properties import INITIAL_VALUES
|
|
from ..images import LinearGradient, RadialGradient
|
|
from .testing_utils import assert_no_logs, capture_logs
|
|
|
|
|
|
def expand_to_dict(css, expected_error=None):
|
|
"""Helper to test shorthand properties expander functions."""
|
|
declarations = tinycss2.parse_declaration_list(css)
|
|
|
|
with capture_logs() as logs:
|
|
base_url = 'http://weasyprint.org/foo/'
|
|
declarations = list(preprocess_declarations(base_url, declarations))
|
|
|
|
if expected_error:
|
|
assert len(logs) == 1
|
|
assert expected_error in logs[0]
|
|
else:
|
|
assert not logs
|
|
|
|
return dict(
|
|
(name, value) for name, value, _priority in declarations
|
|
if value != 'initial')
|
|
|
|
|
|
def assert_invalid(css, message='invalid'):
|
|
assert expand_to_dict(css, message) == {}
|
|
|
|
|
|
@assert_no_logs
|
|
def test_not_print():
|
|
assert expand_to_dict(
|
|
'volume: 42', 'the property does not apply for the print media') == {}
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, values', (
|
|
('1px, 3em, auto, auto', ((1, 'px'), (3, 'em'), 'auto', 'auto')),
|
|
('1px, 3em, auto auto', ((1, 'px'), (3, 'em'), 'auto', 'auto')),
|
|
('1px 3em auto 1px', ((1, 'px'), (3, 'em'), 'auto', (1, 'px'))),
|
|
))
|
|
def test_function(rule, values):
|
|
assert expand_to_dict('clip: rect(%s)' % rule) == {'clip': values}
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'clip: square(1px, 3em, auto, auto)',
|
|
'clip: rect(1px, 3em, auto)',
|
|
'clip: rect(1px, 3em / auto)',
|
|
))
|
|
def test_function_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('counter-reset: foo bar 2 baz', {
|
|
'counter_reset': (('foo', 0), ('bar', 2), ('baz', 0))}),
|
|
('counter-increment: foo bar 2 baz', {
|
|
'counter_increment': (('foo', 1), ('bar', 2), ('baz', 1))}),
|
|
('counter-reset: foo', {'counter_reset': (('foo', 0),)}),
|
|
('counter-reset: FoO', {'counter_reset': (('FoO', 0),)}),
|
|
('counter-increment: foo bAr 2 Bar', {
|
|
'counter_increment': (('foo', 1), ('bAr', 2), ('Bar', 1))}),
|
|
('counter-reset: none', {'counter_reset': ()}),
|
|
))
|
|
def test_counters(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@pytest.mark.parametrize('rule, warning, result', (
|
|
('counter-reset: foo initial', 'Invalid counter name: initial.', {}),
|
|
('counter-reset: foo none', 'Invalid counter name: none.', {}),
|
|
))
|
|
def test_counters_warning(rule, warning, result):
|
|
assert expand_to_dict(rule, warning) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'counter-reset: foo 3px',
|
|
'counter-reset: 3',
|
|
))
|
|
def test_counters_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('letter-spacing: normal', {'letter_spacing': 'normal'}),
|
|
('letter-spacing: 3px', {'letter_spacing': (3, 'px')}),
|
|
('word-spacing: normal', {'word_spacing': 'normal'}),
|
|
('word-spacing: 3px', {'word_spacing': (3, 'px')}),
|
|
))
|
|
def test_spacing(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
def test_spacing_warning():
|
|
assert expand_to_dict(
|
|
'letter_spacing: normal', 'did you mean letter-spacing?') == {}
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'letter-spacing: 3',
|
|
'word-spacing: 3',
|
|
))
|
|
def test_spacing_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('text-decoration-line: none', {'text_decoration_line': 'none'}),
|
|
('text-decoration-line: overline', {'text_decoration_line': {'overline'}}),
|
|
('text-decoration-line: overline blink line-through', {
|
|
'text_decoration_line': {'blink', 'line-through', 'overline'}}),
|
|
))
|
|
def test_decoration_line(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('text-decoration-style: solid', {'text_decoration_style': 'solid'}),
|
|
('text-decoration-style: double', {'text_decoration_style': 'double'}),
|
|
('text-decoration-style: dotted', {'text_decoration_style': 'dotted'}),
|
|
('text-decoration-style: dashed', {'text_decoration_style': 'dashed'}),
|
|
))
|
|
def test_decoration_style(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('text-decoration: none', {'text_decoration_line': 'none'}),
|
|
('text-decoration: overline', {'text_decoration_line': {'overline'}}),
|
|
('text-decoration: overline blink line-through', {
|
|
'text_decoration_line': {'blink', 'line-through', 'overline'}}),
|
|
('text-decoration: red', {'text_decoration_color': (1, 0, 0, 1)}),
|
|
))
|
|
def test_decoration(rule, result):
|
|
default = {
|
|
key: value for key, value in INITIAL_VALUES.items()
|
|
if key.startswith('text_decoration_')}
|
|
real_result = {**default, **result}
|
|
assert expand_to_dict(rule) == real_result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'text-decoration: solid solid',
|
|
'text-decoration: red red',
|
|
'text-decoration: 1px',
|
|
'text-decoration: underline none',
|
|
))
|
|
def test_decoration_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('size: 200px', {'size': ((200, 'px'), (200, 'px'))}),
|
|
('size: 200px 300pt', {'size': ((200, 'px'), (300, 'pt'))}),
|
|
('size: auto', {'size': ((210, 'mm'), (297, 'mm'))}),
|
|
('size: portrait', {'size': ((210, 'mm'), (297, 'mm'))}),
|
|
('size: landscape', {'size': ((297, 'mm'), (210, 'mm'))}),
|
|
('size: A3 portrait', {'size': ((297, 'mm'), (420, 'mm'))}),
|
|
('size: A3 landscape', {'size': ((420, 'mm'), (297, 'mm'))}),
|
|
('size: portrait A3', {'size': ((297, 'mm'), (420, 'mm'))}),
|
|
('size: landscape A3', {'size': ((420, 'mm'), (297, 'mm'))}),
|
|
))
|
|
def test_size(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@pytest.mark.parametrize('rule', (
|
|
'size: A3 landscape A3',
|
|
'size: A9',
|
|
'size: foo',
|
|
'size: foo bar',
|
|
'size: 20%',
|
|
))
|
|
def test_size_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('transform: none', {'transform': ()}),
|
|
('transform: translate(6px) rotate(90deg)', {
|
|
'transform': (
|
|
('translate', ((6, 'px'), (0, 'px'))),
|
|
('rotate', math.pi / 2))}),
|
|
('transform: translate(-4px, 0)', {
|
|
'transform': (('translate', ((-4, 'px'), (0, None))),)}),
|
|
('transform: translate(6px, 20%)', {
|
|
'transform': (('translate', ((6, 'px'), (20, '%'))),)}),
|
|
('transform: scale(2)', {'transform': (('scale', (2, 2)),)}),
|
|
('transform: translate(6px 20%)', {
|
|
'transform': (('translate', ((6, 'px'), (20, '%'))),)}),
|
|
))
|
|
def test_transforms(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'transform: lipsumize(6px)',
|
|
'transform: foo',
|
|
'transform: scale(2) foo',
|
|
'transform: 6px',
|
|
))
|
|
def test_transforms_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('margin: inherit', {
|
|
'margin_top': 'inherit',
|
|
'margin_right': 'inherit',
|
|
'margin_bottom': 'inherit',
|
|
'margin_left': 'inherit',
|
|
}),
|
|
('margin: 1em', {
|
|
'margin_top': (1, 'em'),
|
|
'margin_right': (1, 'em'),
|
|
'margin_bottom': (1, 'em'),
|
|
'margin_left': (1, 'em'),
|
|
}),
|
|
('margin: -1em auto 20%', {
|
|
'margin_top': (-1, 'em'),
|
|
'margin_right': 'auto',
|
|
'margin_bottom': (20, '%'),
|
|
'margin_left': 'auto',
|
|
}),
|
|
('padding: 1em 0', {
|
|
'padding_top': (1, 'em'),
|
|
'padding_right': (0, None),
|
|
'padding_bottom': (1, 'em'),
|
|
'padding_left': (0, None),
|
|
}),
|
|
('padding: 1em 0 2%', {
|
|
'padding_top': (1, 'em'),
|
|
'padding_right': (0, None),
|
|
'padding_bottom': (2, '%'),
|
|
'padding_left': (0, None),
|
|
}),
|
|
('padding: 1em 0 2em 5px', {
|
|
'padding_top': (1, 'em'),
|
|
'padding_right': (0, None),
|
|
'padding_bottom': (2, 'em'),
|
|
'padding_left': (5, 'px'),
|
|
}),
|
|
))
|
|
def test_expand_four_sides(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
def test_expand_four_sides_warning():
|
|
assert expand_to_dict(
|
|
'padding: 1 2 3 4 5', 'Expected 1 to 4 token components got 5') == {}
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'margin: rgb(0, 0, 0)',
|
|
'padding: auto',
|
|
'padding: -12px',
|
|
'border-width: -3em',
|
|
'border-width: 12%',
|
|
))
|
|
def test_expand_four_sides_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('border-top: 3px dotted red', {
|
|
'border_top_width': (3, 'px'),
|
|
'border_top_style': 'dotted',
|
|
'border_top_color': (1, 0, 0, 1), # red
|
|
}),
|
|
('border-top: 3px dotted', {
|
|
'border_top_width': (3, 'px'),
|
|
'border_top_style': 'dotted',
|
|
}),
|
|
('border-top: 3px red', {
|
|
'border_top_width': (3, 'px'),
|
|
'border_top_color': (1, 0, 0, 1), # red
|
|
}),
|
|
('border-top: solid', {'border_top_style': 'solid'}),
|
|
('border: 6px dashed lime', {
|
|
'border_top_width': (6, 'px'),
|
|
'border_top_style': 'dashed',
|
|
'border_top_color': (0, 1, 0, 1), # lime
|
|
|
|
'border_left_width': (6, 'px'),
|
|
'border_left_style': 'dashed',
|
|
'border_left_color': (0, 1, 0, 1), # lime
|
|
|
|
'border_bottom_width': (6, 'px'),
|
|
'border_bottom_style': 'dashed',
|
|
'border_bottom_color': (0, 1, 0, 1), # lime
|
|
|
|
'border_right_width': (6, 'px'),
|
|
'border_right_style': 'dashed',
|
|
'border_right_color': (0, 1, 0, 1), # lime
|
|
}),
|
|
))
|
|
def test_expand_borders(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
def test_expand_borders_invalid():
|
|
assert_invalid('border: 6px dashed left')
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('list-style: inherit', {
|
|
'list_style_position': 'inherit',
|
|
'list_style_image': 'inherit',
|
|
'list_style_type': 'inherit',
|
|
}),
|
|
('list-style: url(../bar/lipsum.png)', {
|
|
'list_style_image': ('url', 'http://weasyprint.org/bar/lipsum.png'),
|
|
}),
|
|
('list-style: square', {
|
|
'list_style_type': 'square',
|
|
}),
|
|
('list-style: circle inside', {
|
|
'list_style_position': 'inside',
|
|
'list_style_type': 'circle',
|
|
}),
|
|
('list-style: none circle inside', {
|
|
'list_style_position': 'inside',
|
|
'list_style_image': ('none', None),
|
|
'list_style_type': 'circle',
|
|
}),
|
|
('list-style: none inside none', {
|
|
'list_style_position': 'inside',
|
|
'list_style_image': ('none', None),
|
|
'list_style_type': 'none',
|
|
}),
|
|
('list-style: inside special none', {
|
|
'list_style_position': 'inside',
|
|
'list_style_image': ('none', None),
|
|
'list_style_type': 'special',
|
|
}),
|
|
))
|
|
def test_expand_list_style(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
def test_expand_list_style_warning():
|
|
assert_invalid(
|
|
'list-style: circle disc',
|
|
'got multiple type values in a list-style shorthand')
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'list-style: none inside none none',
|
|
'list-style: 1px',
|
|
))
|
|
def test_expand_list_style_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
def assert_background(css, **expected):
|
|
"""Helper checking the background properties."""
|
|
expanded = expand_to_dict('background: ' + css)
|
|
assert expanded.pop('background_color') == expected.pop(
|
|
'background_color', INITIAL_VALUES['background_color'])
|
|
nb_layers = len(expanded['background_image'])
|
|
for name, value in expected.items():
|
|
assert expanded.pop(name) == value
|
|
for name, value in expanded.items():
|
|
assert tuple(value) == INITIAL_VALUES[name] * nb_layers
|
|
|
|
|
|
@assert_no_logs
|
|
def test_expand_background():
|
|
assert_background('red', background_color=(1, 0, 0, 1))
|
|
assert_background(
|
|
'url(lipsum.png)',
|
|
background_image=[('url', 'http://weasyprint.org/foo/lipsum.png')])
|
|
assert_background(
|
|
'no-repeat',
|
|
background_repeat=[('no-repeat', 'no-repeat')])
|
|
assert_background('fixed', background_attachment=['fixed'])
|
|
assert_background(
|
|
'repeat no-repeat fixed',
|
|
background_repeat=[('repeat', 'no-repeat')],
|
|
background_attachment=['fixed'])
|
|
assert_background(
|
|
'inherit', background_repeat='inherit',
|
|
background_attachment='inherit', background_image='inherit',
|
|
background_position='inherit', background_size='inherit',
|
|
background_clip='inherit', background_origin='inherit',
|
|
background_color='inherit')
|
|
assert_background(
|
|
'top',
|
|
background_position=[('left', (50, '%'), 'top', (0, '%'))])
|
|
assert_background(
|
|
'top right',
|
|
background_position=[('left', (100, '%'), 'top', (0, '%'))])
|
|
assert_background(
|
|
'top right 20px',
|
|
background_position=[('right', (20, 'px'), 'top', (0, '%'))])
|
|
assert_background(
|
|
'top 1% right 20px',
|
|
background_position=[('right', (20, 'px'), 'top', (1, '%'))])
|
|
assert_background(
|
|
'top no-repeat',
|
|
background_repeat=[('no-repeat', 'no-repeat')],
|
|
background_position=[('left', (50, '%'), 'top', (0, '%'))])
|
|
assert_background(
|
|
'top right no-repeat',
|
|
background_repeat=[('no-repeat', 'no-repeat')],
|
|
background_position=[('left', (100, '%'), 'top', (0, '%'))])
|
|
assert_background(
|
|
'top right 20px no-repeat',
|
|
background_repeat=[('no-repeat', 'no-repeat')],
|
|
background_position=[('right', (20, 'px'), 'top', (0, '%'))])
|
|
assert_background(
|
|
'top 1% right 20px no-repeat',
|
|
background_repeat=[('no-repeat', 'no-repeat')],
|
|
background_position=[('right', (20, 'px'), 'top', (1, '%'))])
|
|
assert_background(
|
|
'url(bar) #f00 repeat-y center left fixed',
|
|
background_color=(1, 0, 0, 1),
|
|
background_image=[('url', 'http://weasyprint.org/foo/bar')],
|
|
background_repeat=[('no-repeat', 'repeat')],
|
|
background_attachment=['fixed'],
|
|
background_position=[('left', (0, '%'), 'top', (50, '%'))])
|
|
assert_background(
|
|
'#00f 10% 200px',
|
|
background_color=(0, 0, 1, 1),
|
|
background_position=[('left', (10, '%'), 'top', (200, 'px'))])
|
|
assert_background(
|
|
'right 78px fixed',
|
|
background_attachment=['fixed'],
|
|
background_position=[('left', (100, '%'), 'top', (78, 'px'))])
|
|
assert_background(
|
|
'center / cover red',
|
|
background_size=['cover'],
|
|
background_position=[('left', (50, '%'), 'top', (50, '%'))],
|
|
background_color=(1, 0, 0, 1))
|
|
assert_background(
|
|
'center / auto red',
|
|
background_size=[('auto', 'auto')],
|
|
background_position=[('left', (50, '%'), 'top', (50, '%'))],
|
|
background_color=(1, 0, 0, 1))
|
|
assert_background(
|
|
'center / 42px',
|
|
background_size=[((42, 'px'), 'auto')],
|
|
background_position=[('left', (50, '%'), 'top', (50, '%'))])
|
|
assert_background(
|
|
'center / 7% 4em',
|
|
background_size=[((7, '%'), (4, 'em'))],
|
|
background_position=[('left', (50, '%'), 'top', (50, '%'))])
|
|
assert_background(
|
|
'red content-box',
|
|
background_color=(1, 0, 0, 1),
|
|
background_origin=['content-box'],
|
|
background_clip=['content-box'])
|
|
assert_background(
|
|
'red border-box content-box',
|
|
background_color=(1, 0, 0, 1),
|
|
background_origin=['border-box'],
|
|
background_clip=['content-box'])
|
|
assert_background(
|
|
'border-box red',
|
|
background_color=(1, 0, 0, 1),
|
|
background_origin=['border-box'])
|
|
assert_background(
|
|
'url(bar) center, no-repeat',
|
|
background_color=(0, 0, 0, 0),
|
|
background_image=[('url', 'http://weasyprint.org/foo/bar'),
|
|
('none', None)],
|
|
background_position=[('left', (50, '%'), 'top', (50, '%')),
|
|
('left', (0, '%'), 'top', (0, '%'))],
|
|
background_repeat=[('repeat', 'repeat'), ('no-repeat', 'no-repeat')])
|
|
# Color must be in the last layer:
|
|
assert_invalid('background: red, url(foo)')
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'background: 10px lipsum',
|
|
'background-position: 10px lipsum',
|
|
'background: content-box red content-box',
|
|
'background-image: inexistent-gradient(blue, green)',
|
|
))
|
|
def test_expand_background_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
def test_expand_background_position():
|
|
"""Test the ``background-position`` property."""
|
|
def position(css, *expected):
|
|
[(name, [value])] = expand_to_dict(
|
|
'background-position:' + css).items()
|
|
assert name == 'background_position'
|
|
assert value == expected
|
|
for css_x, val_x in [
|
|
('left', (0, '%')), ('center', (50, '%')), ('right', (100, '%')),
|
|
('4.5%', (4.5, '%')), ('12px', (12, 'px'))
|
|
]:
|
|
for css_y, val_y in [
|
|
('top', (0, '%')), ('center', (50, '%')), ('bottom', (100, '%')),
|
|
('7%', (7, '%')), ('1.5px', (1.5, 'px'))
|
|
]:
|
|
# Two tokens:
|
|
position('%s %s' % (css_x, css_y), 'left', val_x, 'top', val_y)
|
|
# One token:
|
|
position(css_x, 'left', val_x, 'top', (50, '%'))
|
|
# One token, vertical
|
|
position('top', 'left', (50, '%'), 'top', (0, '%'))
|
|
position('bottom', 'left', (50, '%'), 'top', (100, '%'))
|
|
|
|
# Three tokens:
|
|
position('center top 10%', 'left', (50, '%'), 'top', (10, '%'))
|
|
position('top 10% center', 'left', (50, '%'), 'top', (10, '%'))
|
|
position('center bottom 10%', 'left', (50, '%'), 'bottom', (10, '%'))
|
|
position('bottom 10% center', 'left', (50, '%'), 'bottom', (10, '%'))
|
|
|
|
position('right top 10%', 'right', (0, '%'), 'top', (10, '%'))
|
|
position('top 10% right', 'right', (0, '%'), 'top', (10, '%'))
|
|
position('right bottom 10%', 'right', (0, '%'), 'bottom', (10, '%'))
|
|
position('bottom 10% right', 'right', (0, '%'), 'bottom', (10, '%'))
|
|
|
|
position('center left 10%', 'left', (10, '%'), 'top', (50, '%'))
|
|
position('left 10% center', 'left', (10, '%'), 'top', (50, '%'))
|
|
position('center right 10%', 'right', (10, '%'), 'top', (50, '%'))
|
|
position('right 10% center', 'right', (10, '%'), 'top', (50, '%'))
|
|
|
|
position('bottom left 10%', 'left', (10, '%'), 'bottom', (0, '%'))
|
|
position('left 10% bottom', 'left', (10, '%'), 'bottom', (0, '%'))
|
|
position('bottom right 10%', 'right', (10, '%'), 'bottom', (0, '%'))
|
|
position('right 10% bottom', 'right', (10, '%'), 'bottom', (0, '%'))
|
|
|
|
# Four tokens:
|
|
position('left 10% bottom 3px', 'left', (10, '%'), 'bottom', (3, 'px'))
|
|
position('bottom 3px left 10%', 'left', (10, '%'), 'bottom', (3, 'px'))
|
|
position('right 10% top 3px', 'right', (10, '%'), 'top', (3, 'px'))
|
|
position('top 3px right 10%', 'right', (10, '%'), 'top', (3, 'px'))
|
|
|
|
assert_invalid('background-position: left center 3px')
|
|
assert_invalid('background-position: 3px left')
|
|
assert_invalid('background-position: bottom 4%')
|
|
assert_invalid('background-position: bottom top')
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('border-radius: 1px', {
|
|
'border_top_left_radius': ((1, 'px'), (1, 'px')),
|
|
'border_top_right_radius': ((1, 'px'), (1, 'px')),
|
|
'border_bottom_right_radius': ((1, 'px'), (1, 'px')),
|
|
'border_bottom_left_radius': ((1, 'px'), (1, 'px')),
|
|
}),
|
|
('border-radius: 1px 2em', {
|
|
'border_top_left_radius': ((1, 'px'), (1, 'px')),
|
|
'border_top_right_radius': ((2, 'em'), (2, 'em')),
|
|
'border_bottom_right_radius': ((1, 'px'), (1, 'px')),
|
|
'border_bottom_left_radius': ((2, 'em'), (2, 'em')),
|
|
}),
|
|
('border-radius: 1px / 2em', {
|
|
'border_top_left_radius': ((1, 'px'), (2, 'em')),
|
|
'border_top_right_radius': ((1, 'px'), (2, 'em')),
|
|
'border_bottom_right_radius': ((1, 'px'), (2, 'em')),
|
|
'border_bottom_left_radius': ((1, 'px'), (2, 'em')),
|
|
}),
|
|
('border-radius: 1px 3px / 2em 4%', {
|
|
'border_top_left_radius': ((1, 'px'), (2, 'em')),
|
|
'border_top_right_radius': ((3, 'px'), (4, '%')),
|
|
'border_bottom_right_radius': ((1, 'px'), (2, 'em')),
|
|
'border_bottom_left_radius': ((3, 'px'), (4, '%')),
|
|
}),
|
|
('border-radius: 1px 2em 3%', {
|
|
'border_top_left_radius': ((1, 'px'), (1, 'px')),
|
|
'border_top_right_radius': ((2, 'em'), (2, 'em')),
|
|
'border_bottom_right_radius': ((3, '%'), (3, '%')),
|
|
'border_bottom_left_radius': ((2, 'em'), (2, 'em')),
|
|
}),
|
|
('border-radius: 1px 2em 3% 4rem', {
|
|
'border_top_left_radius': ((1, 'px'), (1, 'px')),
|
|
'border_top_right_radius': ((2, 'em'), (2, 'em')),
|
|
'border_bottom_right_radius': ((3, '%'), (3, '%')),
|
|
'border_bottom_left_radius': ((4, 'rem'), (4, 'rem')),
|
|
}),
|
|
))
|
|
def test_expand_border_radius(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, reason', (
|
|
('border-radius: 1px 1px 1px 1px 1px', '1 to 4 token'),
|
|
('border-radius: 1px 1px 1px 1px 1px / 1px', '1 to 4 token'),
|
|
('border-radius: 1px / 1px / 1px', 'only one "/"'),
|
|
('border-radius: 1px, 1px', 'invalid'),
|
|
('border-radius: 1px /', 'value after "/"'),
|
|
))
|
|
def test_expand_border_radius_invalid(rule, reason):
|
|
assert_invalid(rule, reason)
|
|
|
|
|
|
@assert_no_logs
|
|
def test_font():
|
|
"""Test the ``font`` property."""
|
|
assert expand_to_dict('font: 12px My Fancy Font, serif') == {
|
|
'font_size': (12, 'px'),
|
|
'font_family': ('My Fancy Font', 'serif'),
|
|
}
|
|
assert expand_to_dict('font: small/1.2 "Some Font", serif') == {
|
|
'font_size': 'small',
|
|
'line_height': (1.2, None),
|
|
'font_family': ('Some Font', 'serif'),
|
|
}
|
|
assert expand_to_dict('font: small-caps italic 700 large serif') == {
|
|
'font_style': 'italic',
|
|
'font_variant_caps': 'small-caps',
|
|
'font_weight': 700,
|
|
'font_size': 'large',
|
|
'font_family': ('serif',),
|
|
}
|
|
assert expand_to_dict(
|
|
'font: small-caps condensed normal 700 large serif'
|
|
) == {
|
|
'font_stretch': 'condensed',
|
|
'font_variant_caps': 'small-caps',
|
|
'font_weight': 700,
|
|
'font_size': 'large',
|
|
'font_family': ('serif',),
|
|
}
|
|
assert_invalid('font-family: "My" Font, serif')
|
|
assert_invalid('font-family: "My" "Font", serif')
|
|
assert_invalid('font-family: "My", 12pt, serif')
|
|
assert_invalid('font: menu', 'System fonts are not supported')
|
|
assert_invalid('font: 12deg My Fancy Font, serif')
|
|
assert_invalid('font: 12px')
|
|
assert_invalid('font: 12px/foo serif')
|
|
assert_invalid('font: 12px "Invalid" family')
|
|
assert_invalid('font: normal normal normal normal normal large serif')
|
|
assert_invalid('font: normal small-caps italic 700 condensed large serif')
|
|
assert_invalid('font: small-caps italic 700 normal condensed large serif')
|
|
assert_invalid('font: small-caps italic 700 condensed normal large serif')
|
|
assert_invalid('font: normal normal normal normal')
|
|
assert_invalid('font: normal normal normal italic')
|
|
assert_invalid('font: caption', 'System fonts')
|
|
|
|
|
|
@assert_no_logs
|
|
def test_font_variant():
|
|
"""Test the ``font-variant`` property."""
|
|
assert expand_to_dict('font-variant: normal') == {
|
|
'font_variant_alternates': 'normal',
|
|
'font_variant_caps': 'normal',
|
|
'font_variant_east_asian': 'normal',
|
|
'font_variant_ligatures': 'normal',
|
|
'font_variant_numeric': 'normal',
|
|
'font_variant_position': 'normal',
|
|
}
|
|
assert expand_to_dict('font-variant: none') == {
|
|
'font_variant_alternates': 'normal',
|
|
'font_variant_caps': 'normal',
|
|
'font_variant_east_asian': 'normal',
|
|
'font_variant_ligatures': 'none',
|
|
'font_variant_numeric': 'normal',
|
|
'font_variant_position': 'normal',
|
|
}
|
|
assert expand_to_dict('font-variant: historical-forms petite-caps') == {
|
|
'font_variant_alternates': 'historical-forms',
|
|
'font_variant_caps': 'petite-caps',
|
|
}
|
|
assert expand_to_dict(
|
|
'font-variant: lining-nums contextual small-caps common-ligatures'
|
|
) == {
|
|
'font_variant_ligatures': ('contextual', 'common-ligatures'),
|
|
'font_variant_numeric': ('lining-nums',),
|
|
'font_variant_caps': 'small-caps',
|
|
}
|
|
assert expand_to_dict('font-variant: jis78 ruby proportional-width') == {
|
|
'font_variant_east_asian': ('jis78', 'ruby', 'proportional-width'),
|
|
}
|
|
# CSS2-style font-variant
|
|
assert expand_to_dict('font-variant: small-caps') == {
|
|
'font_variant_caps': 'small-caps',
|
|
}
|
|
assert_invalid('font-variant: normal normal')
|
|
assert_invalid('font-variant: 2')
|
|
assert_invalid('font-variant: ""')
|
|
assert_invalid('font-variant: extra')
|
|
assert_invalid('font-variant: jis78 jis04')
|
|
assert_invalid('font-variant: full-width lining-nums ordinal normal')
|
|
assert_invalid('font-variant: diagonal-fractions stacked-fractions')
|
|
assert_invalid(
|
|
'font-variant: common-ligatures contextual no-common-ligatures')
|
|
assert_invalid('font-variant: sub super')
|
|
assert_invalid('font-variant: slashed-zero slashed-zero')
|
|
|
|
|
|
@assert_no_logs
|
|
def test_line_height():
|
|
"""Test the ``line-height`` property."""
|
|
assert expand_to_dict('line-height: 1px') == {'line_height': (1, 'px')}
|
|
assert expand_to_dict('line-height: 1.1%') == {'line_height': (1.1, '%')}
|
|
assert expand_to_dict('line-height: 1em') == {'line_height': (1, 'em')}
|
|
assert expand_to_dict('line-height: 1') == {'line_height': (1, None)}
|
|
assert expand_to_dict('line-height: 1.3') == {'line_height': (1.3, None)}
|
|
assert expand_to_dict('line-height: -0') == {'line_height': (0, None)}
|
|
assert expand_to_dict('line-height: 0px') == {'line_height': (0, 'px')}
|
|
assert_invalid('line-height: 1deg')
|
|
assert_invalid('line-height: -1px')
|
|
assert_invalid('line-height: -1')
|
|
assert_invalid('line-height: -0.5%')
|
|
assert_invalid('line-height: 1px 1px')
|
|
|
|
|
|
@assert_no_logs
|
|
def test_string_set():
|
|
"""Test the ``string-set`` property."""
|
|
assert expand_to_dict('string-set: test content(text)') == {
|
|
'string_set': (('test', (('content()', 'text'),)),)}
|
|
assert expand_to_dict('string-set: test content(before)') == {
|
|
'string_set': (('test', (('content()', 'before'),)),)}
|
|
assert expand_to_dict('string-set: test "string"') == {
|
|
'string_set': (('test', (('string', 'string'),)),)}
|
|
assert expand_to_dict(
|
|
'string-set: test1 "string", test2 "string"') == {
|
|
'string_set': (
|
|
('test1', (('string', 'string'),)),
|
|
('test2', (('string', 'string'),)))}
|
|
assert expand_to_dict('string-set: test attr(class)') == {
|
|
'string_set': (('test', (('attr()', ('class', 'string', '')),)),)}
|
|
assert expand_to_dict('string-set: test counter(count)') == {
|
|
'string_set': (('test', (('counter()', ('count', 'decimal')),)),)}
|
|
assert expand_to_dict(
|
|
'string-set: test counter(count, upper-roman)') == {
|
|
'string_set': (
|
|
('test', (('counter()', ('count', 'upper-roman')),)),)}
|
|
assert expand_to_dict('string-set: test counters(count, ".")') == {
|
|
'string_set': (
|
|
('test', (('counters()', ('count', '.', 'decimal')),)),)}
|
|
assert expand_to_dict(
|
|
'string-set: test counters(count, ".", upper-roman)') == {
|
|
'string_set': (
|
|
('test', (('counters()', ('count', '.', 'upper-roman')),)),)}
|
|
assert expand_to_dict(
|
|
'string-set: test content(text) "string" '
|
|
'attr(title) attr(title) counter(count)') == {
|
|
'string_set': (('test', (
|
|
('content()', 'text'), ('string', 'string'),
|
|
('attr()', ('title', 'string', '')),
|
|
('attr()', ('title', 'string', '')),
|
|
('counter()', ('count', 'decimal')))),)}
|
|
|
|
assert_invalid('string-set: test')
|
|
assert_invalid('string-set: test test1')
|
|
assert_invalid('string-set: test content(test)')
|
|
assert_invalid('string-set: test unknown()')
|
|
assert_invalid('string-set: test attr(id, class)')
|
|
|
|
|
|
@assert_no_logs
|
|
def test_linear_gradient():
|
|
red = (1, 0, 0, 1)
|
|
lime = (0, 1, 0, 1)
|
|
blue = (0, 0, 1, 1)
|
|
pi = math.pi
|
|
|
|
def gradient(css, direction, colors=[blue], stop_positions=[None]):
|
|
for repeating, prefix in ((False, ''), (True, 'repeating-')):
|
|
expanded = expand_to_dict(
|
|
'background-image: %slinear-gradient(%s)' % (prefix, css))
|
|
[(_, [(type_, image)])] = expanded.items()
|
|
assert type_ == 'linear-gradient'
|
|
assert isinstance(image, LinearGradient)
|
|
assert image.repeating == repeating
|
|
assert image.direction_type == direction[0]
|
|
if isinstance(image.direction, str):
|
|
image.direction == direction[1]
|
|
else:
|
|
assert image.direction == pytest.approx(direction[1])
|
|
assert image.colors == colors
|
|
assert image.stop_positions == stop_positions
|
|
|
|
def invalid(css):
|
|
assert_invalid('background-image: linear-gradient(%s)' % css)
|
|
assert_invalid('background-image: repeating-linear-gradient(%s)' % css)
|
|
|
|
invalid(' ')
|
|
invalid('1% blue')
|
|
invalid('blue 10deg')
|
|
invalid('blue 4')
|
|
invalid('soylent-green 4px')
|
|
invalid('red 4px 2px')
|
|
gradient('blue', ('angle', pi))
|
|
gradient('red', ('angle', pi), [red], [None])
|
|
gradient('blue 1%, lime,red 2em ', ('angle', pi),
|
|
[blue, lime, red], [(1, '%'), None, (2, 'em')])
|
|
invalid('18deg')
|
|
gradient('18deg, blue', ('angle', pi / 10))
|
|
gradient('4rad, blue', ('angle', 4))
|
|
gradient('.25turn, blue', ('angle', pi / 2))
|
|
gradient('100grad, blue', ('angle', pi / 2))
|
|
gradient('12rad, blue 1%, lime,red 2em ', ('angle', 12),
|
|
[blue, lime, red], [(1, '%'), None, (2, 'em')])
|
|
invalid('10arc-minutes, blue')
|
|
invalid('10px, blue')
|
|
invalid('to 90deg, blue')
|
|
gradient('to top, blue', ('angle', 0))
|
|
gradient('to right, blue', ('angle', pi / 2))
|
|
gradient('to bottom, blue', ('angle', pi))
|
|
gradient('to left, blue', ('angle', pi * 3 / 2))
|
|
gradient('to right, blue 1%, lime,red 2em ', ('angle', pi / 2),
|
|
[blue, lime, red], [(1, '%'), None, (2, 'em')])
|
|
invalid('to the top, blue')
|
|
invalid('to up, blue')
|
|
invalid('into top, blue')
|
|
invalid('top, blue')
|
|
gradient('to top left, blue', ('corner', 'top_left'))
|
|
gradient('to left top, blue', ('corner', 'top_left'))
|
|
gradient('to top right, blue', ('corner', 'top_right'))
|
|
gradient('to right top, blue', ('corner', 'top_right'))
|
|
gradient('to bottom left, blue', ('corner', 'bottom_left'))
|
|
gradient('to left bottom, blue', ('corner', 'bottom_left'))
|
|
gradient('to bottom right, blue', ('corner', 'bottom_right'))
|
|
gradient('to right bottom, blue', ('corner', 'bottom_right'))
|
|
invalid('to bottom up, blue')
|
|
invalid('bottom left, blue')
|
|
|
|
|
|
@assert_no_logs
|
|
def test_overflow_wrap():
|
|
assert expand_to_dict('overflow-wrap: normal') == {
|
|
'overflow_wrap': 'normal'}
|
|
assert expand_to_dict('overflow-wrap: break-word') == {
|
|
'overflow_wrap': 'break-word'}
|
|
assert_invalid('overflow-wrap: none')
|
|
assert_invalid('overflow-wrap: normal, break-word')
|
|
|
|
|
|
@assert_no_logs
|
|
def test_expand_word_wrap():
|
|
assert expand_to_dict('word-wrap: normal') == {
|
|
'overflow_wrap': 'normal'}
|
|
assert expand_to_dict('word-wrap: break-word') == {
|
|
'overflow_wrap': 'break-word'}
|
|
assert_invalid('word-wrap: none')
|
|
assert_invalid('word-wrap: normal, break-word')
|
|
|
|
|
|
@assert_no_logs
|
|
def test_radial_gradient():
|
|
red = (1, 0, 0, 1)
|
|
lime = (0, 1, 0, 1)
|
|
blue = (0, 0, 1, 1)
|
|
|
|
def gradient(css, shape='ellipse', size=('keyword', 'farthest-corner'),
|
|
center=('left', (50, '%'), 'top', (50, '%')),
|
|
colors=[blue], stop_positions=[None]):
|
|
for repeating, prefix in ((False, ''), (True, 'repeating-')):
|
|
expanded = expand_to_dict(
|
|
'background-image: %sradial-gradient(%s)' % (prefix, css))
|
|
[(_, [(type_, image)])] = expanded.items()
|
|
assert type_ == 'radial-gradient'
|
|
assert isinstance(image, RadialGradient)
|
|
assert image.repeating == repeating
|
|
assert image.shape == shape
|
|
assert image.size_type == size[0]
|
|
assert image.size == size[1]
|
|
assert image.center == center
|
|
assert image.colors == colors
|
|
assert image.stop_positions == stop_positions
|
|
|
|
def invalid(css):
|
|
assert_invalid('background-image: radial-gradient(%s)' % css)
|
|
assert_invalid('background-image: repeating-radial-gradient(%s)' % css)
|
|
|
|
invalid(' ')
|
|
invalid('1% blue')
|
|
invalid('blue 10deg')
|
|
invalid('blue 4')
|
|
invalid('soylent-green 4px')
|
|
invalid('red 4px 2px')
|
|
gradient('blue')
|
|
gradient('red', colors=[red])
|
|
gradient('blue 1%, lime,red 2em ', colors=[blue, lime, red],
|
|
stop_positions=[(1, '%'), None, (2, 'em')])
|
|
gradient('circle, blue', 'circle')
|
|
gradient('ellipse, blue', 'ellipse')
|
|
invalid('circle')
|
|
invalid('square, blue')
|
|
invalid('closest-triangle, blue')
|
|
invalid('center, blue')
|
|
gradient('ellipse closest-corner, blue',
|
|
'ellipse', ('keyword', 'closest-corner'))
|
|
gradient('circle closest-side, blue',
|
|
'circle', ('keyword', 'closest-side'))
|
|
gradient('farthest-corner circle, blue',
|
|
'circle', ('keyword', 'farthest-corner'))
|
|
gradient('farthest-side, blue',
|
|
'ellipse', ('keyword', 'farthest-side'))
|
|
gradient('5ch, blue',
|
|
'circle', ('explicit', ((5, 'ch'), (5, 'ch'))))
|
|
gradient('5ch circle, blue',
|
|
'circle', ('explicit', ((5, 'ch'), (5, 'ch'))))
|
|
gradient('circle 5ch, blue',
|
|
'circle', ('explicit', ((5, 'ch'), (5, 'ch'))))
|
|
invalid('ellipse 5ch')
|
|
invalid('5ch ellipse')
|
|
gradient('10px 50px, blue',
|
|
'ellipse', ('explicit', ((10, 'px'), (50, 'px'))))
|
|
gradient('10px 50px ellipse, blue',
|
|
'ellipse', ('explicit', ((10, 'px'), (50, 'px'))))
|
|
gradient('ellipse 10px 50px, blue',
|
|
'ellipse', ('explicit', ((10, 'px'), (50, 'px'))))
|
|
invalid('circle 10px 50px, blue')
|
|
invalid('10px 50px circle, blue')
|
|
invalid('10%, blue')
|
|
invalid('10% circle, blue')
|
|
invalid('circle 10%, blue')
|
|
gradient('10px 50px, blue',
|
|
'ellipse', ('explicit', ((10, 'px'), (50, 'px'))))
|
|
invalid('at appex, blue')
|
|
gradient('at top 10% right, blue',
|
|
center=('right', (0, '%'), 'top', (10, '%')))
|
|
gradient('circle at bottom, blue', shape='circle',
|
|
center=('left', (50, '%'), 'top', (100, '%')))
|
|
gradient('circle at 10px, blue', shape='circle',
|
|
center=('left', (10, 'px'), 'top', (50, '%')))
|
|
gradient('closest-side circle at right 5em, blue',
|
|
shape='circle', size=('keyword', 'closest-side'),
|
|
center=('left', (100, '%'), 'top', (5, 'em')))
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('flex: auto', {
|
|
'flex_grow': 1,
|
|
'flex_shrink': 1,
|
|
'flex_basis': 'auto',
|
|
}),
|
|
('flex: none', {
|
|
'flex_grow': 0,
|
|
'flex_shrink': 0,
|
|
'flex_basis': 'auto',
|
|
}),
|
|
('flex: 10', {
|
|
'flex_grow': 10,
|
|
'flex_shrink': 1,
|
|
'flex_basis': ZERO_PIXELS,
|
|
}),
|
|
('flex: 2 2', {
|
|
'flex_grow': 2,
|
|
'flex_shrink': 2,
|
|
'flex_basis': ZERO_PIXELS,
|
|
}),
|
|
('flex: 2 2 1px', {
|
|
'flex_grow': 2,
|
|
'flex_shrink': 2,
|
|
'flex_basis': (1, 'px'),
|
|
}),
|
|
('flex: 2 2 auto', {
|
|
'flex_grow': 2,
|
|
'flex_shrink': 2,
|
|
'flex_basis': 'auto',
|
|
}),
|
|
('flex: 2 auto', {
|
|
'flex_grow': 2,
|
|
'flex_shrink': 1,
|
|
'flex_basis': 'auto',
|
|
}),
|
|
('flex: 0 auto', {
|
|
'flex_grow': 0,
|
|
'flex_shrink': 1,
|
|
'flex_basis': 'auto',
|
|
}),
|
|
))
|
|
def test_flex(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'flex: auto 0 0 0',
|
|
'flex: 1px 2px',
|
|
'flex: auto auto',
|
|
'flex: auto 1 auto',
|
|
))
|
|
def test_flex_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('flex-flow: column', {
|
|
'flex_direction': 'column',
|
|
}),
|
|
('flex-flow: wrap', {
|
|
'flex_wrap': 'wrap',
|
|
}),
|
|
('flex-flow: wrap column', {
|
|
'flex_direction': 'column',
|
|
'flex_wrap': 'wrap',
|
|
}),
|
|
('flex-flow: row wrap', {
|
|
'flex_direction': 'row',
|
|
'flex_wrap': 'wrap',
|
|
}),
|
|
))
|
|
def test_flex_flow(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'flex-flow: 1px',
|
|
'flex-flow: wrap 1px',
|
|
'flex-flow: row row',
|
|
'flex-flow: wrap nowrap',
|
|
'flex-flow: column wrap nowrap row',
|
|
))
|
|
def test_flex_flow_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'list-style-type: symbols()',
|
|
'list-style-type: symbols(cyclic)',
|
|
'list-style-type: symbols(symbolic)',
|
|
'list-style-type: symbols(fixed)',
|
|
'list-style-type: symbols(alphabetic "a")',
|
|
'list-style-type: symbols(numeric "1")',
|
|
'list-style-type: symbols(test "a" "b")',
|
|
'list-style-type: symbols(fixed symbolic "a" "b")',
|
|
))
|
|
def test_function_symbols(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('page-break-after: left', {'break_after': 'left'}),
|
|
('page-break-before: always', {'break_before': 'page'}),
|
|
))
|
|
def test_page_break(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'page-break-after: top',
|
|
'page-break-before: 1px',
|
|
))
|
|
def test_page_break_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('page-break-inside: avoid', {'break_inside': 'avoid'}),
|
|
))
|
|
def test_page_break_inside(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule', (
|
|
'page-break-inside: top',
|
|
))
|
|
def test_page_break_inside_invalid(rule):
|
|
assert_invalid(rule)
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, result', (
|
|
('columns: 1em', {'column_width': (1, 'em'), 'column_count': 'auto'}),
|
|
('columns: auto', {'column_width': 'auto', 'column_count': 'auto'}),
|
|
('columns: auto auto', {'column_width': 'auto', 'column_count': 'auto'}),
|
|
))
|
|
def test_columns(rule, result):
|
|
assert expand_to_dict(rule) == result
|
|
|
|
|
|
@assert_no_logs
|
|
@pytest.mark.parametrize('rule, reason', (
|
|
('columns: 1px 2px', 'invalid'),
|
|
('columns: auto auto auto', 'multiple'),
|
|
))
|
|
def test_columns_invalid(rule, reason):
|
|
assert_invalid(rule, reason)
|