def test_overflow_outside_parent(self): grandchild1 = TestNode( name='div', style=CSS(display=BLOCK, height=10, margin=50), ) grandchild2 = TestNode( name='div', style=CSS(display=BLOCK, height=10, margin=40), ) child = TestNode( name='div', style=CSS(display=BLOCK, height=10, margin=10), children=[grandchild1, grandchild2] ) root = TestNode( name='div', style=CSS(display=BLOCK), children=[child] ) layout(self.display, root) self.assertLayout( root, { 'tag': 'div', 'border_box': {'size': (1024, 708), 'position': (0, 50)}, 'padding_box': {'size': (1024, 708), 'position': (0, 50)}, 'content': {'size': (1024, 708), 'position': (0, 50)}, 'children': [ { 'tag': 'div', 'border_box': {'size': (1004, 10), 'position': (10, 50)}, 'padding_box': {'size': (1004, 10), 'position': (10, 50)}, 'content': {'size': (1004, 10), 'position': (10, 50)}, 'children': [ { 'tag': 'div', 'border_box': {'size': (904, 10), 'position': (60, 50)}, 'padding_box': {'size': (904, 10), 'position': (60, 50)}, 'content': {'size': (904, 10), 'position': (60, 50)}, }, { 'tag': 'div', 'border_box': {'size': (924, 10), 'position': (50, 110)}, 'padding_box': {'size': (924, 10), 'position': (50, 110)}, 'content': {'size': (924, 10), 'position': (50, 110)}, } ], } ], } )
def test_collapsed_margins(self): child1 = TestNode( name='div', style=CSS(display=BLOCK, height=10, margin=10), ) child2 = TestNode( name='div', style=CSS(display=BLOCK, height=10, margin=30), ) child3 = TestNode( name='div', style=CSS(display=BLOCK, height=10, margin=20), ) root = TestNode( name='div', style=CSS(display=BLOCK), children=[child1, child2, child3] ) layout(self.display, root) self.assertLayout( root, { 'tag': 'div', 'border_box': {'position': (0, 10), 'size': (1024, 738)}, 'padding_box': {'position': (0, 10), 'size': (1024, 738)}, 'content': {'position': (0, 10), 'size': (1024, 738)}, 'children': [ { 'tag': 'div', 'border_box': {'position': (10, 10), 'size': (1004, 10)}, 'padding_box': {'position': (10, 10), 'size': (1004, 10)}, 'content': {'position': (10, 10), 'size': (1004, 10)}, }, { 'tag': 'div', 'border_box': {'position': (30, 50), 'size': (964, 10)}, 'padding_box': {'position': (30, 50), 'size': (964, 10)}, 'content': {'position': (30, 50), 'size': (964, 10)}, }, { 'tag': 'div', 'border_box': {'position': (20, 90), 'size': (984, 10)}, 'padding_box': {'position': (20, 90), 'size': (984, 10)}, 'content': {'position': (20, 90), 'size': (984, 10)}, } ], } )
def test_simple_vertical(self): child1 = TestNode( name='div', style=CSS(display=BLOCK, height=10), ) child2 = TestNode( name='div', style=CSS(display=BLOCK, height=10), ) child3 = TestNode( name='div', style=CSS(display=BLOCK, height=10), ) root = TestNode( name='div', style=CSS(display=BLOCK), children=[child1, child2, child3] ) layout(self.display, root) self.assertLayout( root, { 'tag': 'div', 'border_box': {'position': (0, 0), 'size': (1024, 768)}, 'padding_box': {'position': (0, 0), 'size': (1024, 768)}, 'content': {'position': (0, 0), 'size': (1024, 768)}, 'children': [ { 'tag': 'div', 'border_box': {'position': (0, 0), 'size': (1024, 10)}, 'padding_box': {'position': (0, 0), 'size': (1024, 10)}, 'content': {'position': (0, 0), 'size': (1024, 10)}, }, { 'tag': 'div', 'border_box': {'position': (0, 10), 'size': (1024, 10)}, 'padding_box': {'position': (0, 10), 'size': (1024, 10)}, 'content': {'position': (0, 10), 'size': (1024, 10)}, }, { 'tag': 'div', 'border_box': {'position': (0, 20), 'size': (1024, 10)}, 'padding_box': {'position': (0, 20), 'size': (1024, 10)}, 'content': {'position': (0, 20), 'size': (1024, 10)}, } ], } )
def test_auto_left_margin(self): node = TestNode(name='img', style=CSS(display=INLINE, margin_left=AUTO)) node.intrinsic.width = 50 node.intrinsic.height = 10 node.intrinsic.is_replaced = True self.layout_node(node) self.assertLayout( node, { 'tag': 'img', 'border_box': { 'position': (0, 0), 'size': (50, 10) }, 'padding_box': { 'position': (0, 0), 'size': (50, 10) }, 'content': { 'position': (0, 0), 'size': (50, 10) }, })
def test_str(self): node = TestNode(style=CSS()) node.layout.dirty = None node.style.update( width=10, height=20, margin=(30, 40, 50, 60), display=BLOCK, cursor=['url(some.cursor.uri)', AUTO] ) print(str(node.style)) self.assertEqual( str(node.style), 'cursor: url("some.cursor.uri"), auto; ' "display: block; " "height: 20px; " "margin-bottom: 50px; " "margin-left: 60px; " "margin-right: 40px; " "margin-top: 30px; " "width: 10px" )
def test_left_and_right_margin(self): node = TestNode(name='div', style=CSS(display=BLOCK, height=10, margin_left=AUTO, margin_right=AUTO)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (0, 0), 'size': (1024, 10) }, 'padding_box': { 'position': (0, 0), 'size': (1024, 10) }, 'content': { 'position': (0, 0), 'size': (1024, 10) }, })
def test_medium_border(self): node = TestNode(name='div', style=CSS(display=BLOCK, border_style=SOLID, border_width=MEDIUM, width=50, height=30)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (0, 0), 'size': (60, 40) }, 'padding_box': { 'position': (5, 5), 'size': (50, 30) }, 'content': { 'position': (5, 5), 'size': (50, 30) }, })
def test_set_multiple_properties(self): node = TestNode(style=CSS()) node.layout.dirty = None node.style.update(width=10, height=20) self.assertEqual(node.style.width, 10) self.assertEqual(node.style.height, 20) self.assertIs(node.style.top, AUTO) self.assertTrue(node.style.dirty) # Clear properties node.style.update(width=None, top=30) self.assertIs(node.style.width, AUTO) self.assertEqual(node.style.height, 20) self.assertEqual(node.style.top, 30) self.assertTrue(node.style.dirty) # Clean the layout node.layout.dirty = False # Setting a non-property with self.assertRaises(NameError): node.style.update(not_a_property=10) self.assertFalse(node.style.dirty)
def test_border_shorthands_valid_set_subproperties_get_shorthand_property( self): for direction in ['bottom', 'left', 'right', 'top']: node = TestNode(style=CSS()) node.layout.dirty = None setattr(node.style, 'border_{direction}_width'.format(direction=direction), 'thick') setattr(node.style, 'border_{direction}_style'.format(direction=direction), 'solid') setattr(node.style, 'border_{direction}_color'.format(direction=direction), 'black') shorthand = 'border_{direction}'.format(direction=direction) self.assertEqual(str(getattr(node.style, shorthand)), 'thick solid rgba(0, 0, 0, 1.0)') self.assertEqual( str(node.style), "border-{direction}-color: rgba(0, 0, 0, 1.0); " "border-{direction}-style: solid; " "border-{direction}-width: thick" "".format(direction=direction))
def test_property_border_spacing_valid_sequence_2_items(self): node = TestNode(style=CSS()) node.layout.dirty = None # List of strings node.style.border_spacing = ['1', '2'] self.assertEqual(node.style.border_spacing.horizontal, 1 * px) self.assertEqual(node.style.border_spacing.vertical, 2 * px) self.assertEqual(repr(node.style.border_spacing), 'BorderSpacing(1px, 2px)') self.assertEqual(str(node.style.border_spacing), '1px 2px') # List node.style.border_spacing = [1, 2] self.assertEqual(node.style.border_spacing.horizontal, 1 * px) self.assertEqual(node.style.border_spacing.vertical, 2 * px) self.assertEqual(repr(node.style.border_spacing), 'BorderSpacing(1px, 2px)') self.assertEqual(str(node.style.border_spacing), '1px 2px') # Tuple node.style.border_spacing = (1, 2) self.assertEqual(node.style.border_spacing.horizontal, 1 * px) self.assertEqual(node.style.border_spacing.vertical, 2 * px) self.assertEqual(repr(node.style.border_spacing), 'BorderSpacing(1px, 2px)') self.assertEqual(str(node.style.border_spacing), '1px 2px')
def test_shorthand_valid_border_property_resets(self): node = TestNode(style=CSS()) node.layout.dirty = None node.style.border_color = "black" node.style.border_style = "solid" node.style.border_width = "thick" self.assertEqual( str(node.style.border_color), '(rgba(0, 0, 0, 1.0), ' 'rgba(0, 0, 0, 1.0), ' 'rgba(0, 0, 0, 1.0), ' 'rgba(0, 0, 0, 1.0))') self.assertEqual(node.style.border_style, ('solid', 'solid', 'solid', 'solid')) self.assertEqual(node.style.border_width, ('thick', 'thick', 'thick', 'thick')) # This should reset all other properties to their initial values node.style.border = "red" self.assertEqual( str(node.style.border_color), '(rgba(255, 0, 0, 1.0), ' 'rgba(255, 0, 0, 1.0), ' 'rgba(255, 0, 0, 1.0), ' 'rgba(255, 0, 0, 1.0))') self.assertEqual(node.style.border_style, (None, None, None, None)) self.assertEqual(str(node.style.border_width), '(0px, 0px, 0px, 0px)')
def test_width_exceeds_parent(self): node = TestNode(name='div', style=CSS(display=BLOCK, width=500, height=20, padding=50, border_width=60, border_style=SOLID, margin=70)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (70, 70), 'size': (720, 240) }, 'padding_box': { 'position': (130, 130), 'size': (600, 120) }, 'content': { 'position': (180, 180), 'size': (500, 20) }, })
def test_thin_border(self): node = TestNode(name='div', style=CSS(display=BLOCK, border_style=SOLID, border_width=THIN, width=50, height=30)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (0, 0), 'size': (52, 32) }, 'padding_box': { 'position': (1, 1), 'size': (50, 30) }, 'content': { 'position': (1, 1), 'size': (50, 30) }, })
def test_width_fixed_left_and_right_margin_rtl(self): node = TestNode(name='div', style=CSS(display=BLOCK, width=50, height=10, margin_left=30, margin_right=40, direction=RTL)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (934, 0), 'size': (50, 10) }, 'padding_box': { 'position': (934, 0), 'size': (50, 10) }, 'content': { 'position': (934, 0), 'size': (50, 10) }, })
def test_width_exceeds_parent_auto_left_and_right_margins(self): node = TestNode(name='div', style=CSS(display=BLOCK, width=500, height=20, padding=50, border_width=60, border_style=SOLID, margin_left=AUTO, margin_right=AUTO)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (152, 0), 'size': (720, 240) }, 'padding_box': { 'position': (212, 60), 'size': (600, 120) }, 'content': { 'position': (262, 110), 'size': (500, 20) }, })
def test_height_auto_bottom_margin(self): node = TestNode(name='div', style=CSS(display=BLOCK, width=10, height=50, margin_bottom=AUTO)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (0, 0), 'size': (10, 50) }, 'padding_box': { 'position': (0, 0), 'size': (10, 50) }, 'content': { 'position': (0, 0), 'size': (10, 50) }, })
def test_width_auto_left_margin(self): node = TestNode(name='div', style=CSS(display=BLOCK, width=50, height=10, margin_left=AUTO)) self.layout_node(node) self.assertLayout( node, { 'tag': 'div', 'border_box': { 'position': (974, 0), 'size': (50, 10) }, 'padding_box': { 'position': (974, 0), 'size': (50, 10) }, 'content': { 'position': (974, 0), 'size': (50, 10) }, })
def test_validated_property_cursor_set_invalid_list_values(self): node = TestNode(style=CSS()) node.layout.dirty = None with self.assertRaises(ValueError): node.style.cursor = ['boom'] with self.assertRaises(ValueError): node.style.cursor = [AUTO, 'url(google.com)']
def test_property_border_spacing_invalid_separator_str_2_items(self): node = TestNode(style=CSS()) node.layout.dirty = None with self.assertRaises(ValueError): node.style.border_spacing = '1, 2' with self.assertRaises(ValueError): node.style.border_spacing = '1; 2'
def test_property_border_spacing_valid_str_2_items_px(self): node = TestNode(style=CSS()) node.layout.dirty = None node.style.border_spacing = '1px 2px' self.assertEqual(node.style.border_spacing.horizontal, 1 * px) self.assertEqual(node.style.border_spacing.vertical, 2 * px) self.assertEqual(repr(node.style.border_spacing), 'BorderSpacing(1px, 2px)') self.assertEqual(str(node.style.border_spacing), '1px 2px')
def test_shorthand_invalid_outline_property_values(self): node = TestNode(style=CSS()) node.layout.dirty = None with self.assertRaises(ValueError): node.style.outline = "foo bar spam" with self.assertRaises(ValueError): node.style.outline = "foo", "bar", "spam"
def test_shorthand_valid_outline_property_list_sets_shorthand_subproperties(self): node = TestNode(style=CSS()) node.layout.dirty = None node.style.outline = "black", "solid", "thick" self.assertEqual(str(node.style.outline_color), 'rgba(0, 0, 0, 1.0)') self.assertEqual(str(node.style.outline_style), 'solid') self.assertEqual(str(node.style.outline_width), 'thick')
def test_property_border_spacing_valid_str_1_item_inherit(self): node = TestNode(style=CSS()) node.layout.dirty = None # Text value node.style.border_spacing = 'inherit' self.assertEqual(node.style.border_spacing, 'inherit') self.assertEqual(node.style.border_spacing, 'inherit') self.assertNotIsInstance(node.style.border_spacing, BorderSpacing)
def test_border_shorthands_valid_initial_values(self): node = TestNode(style=CSS()) node.layout.dirty = None for property_name in [ 'border', 'border_bottom', 'border_left', 'border_right', 'border_top' ]: self.assertEqual(str(getattr(node.style, property_name)), '')
def test_border_shorthands_invalid_values(self): for direction in ['', '_bottom', '_left', '_right', '_top']: node = TestNode(style=CSS()) node.layout.dirty = None with self.assertRaises(ValueError): setattr(node.style, 'border' + direction, 'foo bar spam') with self.assertRaises(ValueError): setattr(node.style, 'border' + direction, ("foo", "bar", "spam"))
def test_other_property_callable_valid(self): node = TestNode(style=CSS()) node.layout.dirty = None # Check initial value LTR self.assertEqual(node.style.text_align, LEFT) # Change direction to RTL node.style.update(direction=RTL) self.assertEqual(node.style.text_align, RIGHT)
def test_dict(self): "Style declarations expose a dict-like interface" node = TestNode(style=CSS()) node.layout.dirty = None node.style.update( width=10, height=20, margin=(30, 40, 50, 60), display=BLOCK ) self.assertEqual( node.style.keys(), {'display', 'height', 'margin_bottom', 'margin_left', 'margin_right', 'margin_top', 'width'} ) self.assertEqual( sorted(node.style.items()), sorted([ ('display', BLOCK), ('height', 20), ('margin_bottom', 50), ('margin_left', 60), ('margin_right', 40), ('margin_top', 30), ('width', 10), ]) ) # A property can be set, retrieved and cleared using the CSS attribute name node.style['margin-bottom'] = 10 self.assertEqual(node.style['margin-bottom'], 10) del node.style['margin-bottom'] self.assertEqual(node.style['margin-bottom'], 0) # A property can be set, retrieved and cleared using the Python attribute name node.style['margin_bottom'] = 10 self.assertEqual(node.style['margin_bottom'], 10) del node.style['margin_bottom'] self.assertEqual(node.style['margin_bottom'], 0) # Clearing a valid property isn't an error del node.style['margin_bottom'] self.assertEqual(node.style['margin_bottom'], 0) # Non-existent properties raise KeyError with self.assertRaises(KeyError): node.style['no-such-property'] = 'no-such-value' with self.assertRaises(KeyError): node.style['no-such-property'] with self.assertRaises(KeyError): del node.style['no-such-property']
def test_shorthand_valid_outline_subproperties_set_shorthand_property(self): node = TestNode(style=CSS()) node.layout.dirty = None node.style.outline_color = "black" node.style.outline_style = "solid" node.style.outline_width = "thick" self.assertEqual(str(node.style.outline), 'rgba(0, 0, 0, 1.0) solid thick') self.assertEqual(str(node.style), "outline-color: rgba(0, 0, 0, 1.0); outline-style: solid; outline-width: thick")
def __init__(self, name=None, style=None, children=None): self.name = name if name else 'div' self.parent = None self.children = [] if children: for child in children: self.children.append(child) child.parent = self self.intrinsic = Size(self) self.layout = Box(self) self.style = style.copy(self) if style else CSS()
def test_quotes_invalid_empty(self): node = TestNode(style=CSS()) node.layout.dirty = None with self.assertRaises(ValueError): node.style.quotes = '' with self.assertRaises(ValueError): node.style.quotes = [] with self.assertRaises(ValueError): node.style.quotes = [()]