def test_blank_name(): css = {"% ": {"margin": "1px"}} with pytest.raises(ValueError) as raised: render_css(css) assert str( raised.value) == "An extend must have a name: it cannot be `%` alone"
def test_using_undefined_name(): css = { ".foo": extend('my-extend'), } with pytest.raises(ValueError) as raised: render_css(css) assert str( raised.value ) == "Cannot extend `.foo` with undefined/not yet defined `%my-extend`"
def test_using_name_not_yet_defined(): css = { ".foo": extend('my-extend'), '%my-extend': { "margin": "1px" }, } with pytest.raises(ValueError) as raised: render_css(css) assert str( raised.value ) == "Cannot extend `.foo` with undefined/not yet defined `%my-extend`"
def test_use_space_to_repeat_selectors(): css = { ".foo": { "padding": "1px" }, ".bar .foo": { "padding": "2px" }, ".foo ": { # added space to repeat the selector, as order is important in css "padding": "3px" }, } assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { padding: 1px; } .bar .foo { padding: 2px; } .foo { padding: 3px; } """)
def test_at_rule(): ext1 = {"color": "ext1"} css = { ".foo": { "margin": "2px" }, "%ext2": { "color": "ext2" }, # all ext2 usages are here, except from media ".bar": extend(ext1), # all ext1 usages are here, except from media "@media all": { # all ext2 in media are before, because named extend "%ext3": { "color": "ext3" }, # all ext3 in media are here ".barbar": extend(ext1), # all ext1 in media are here, because dict extend ".bazbaz": { ".qux1": { "margin": "33px" }, ".qux2": extend(ext1, "ext2", "ext3", css={"margin": "44px"}), } }, ".baz": { "a": { "margin": "3px" }, "b": extend(ext1, "ext2", css={"margin": "4px"}), } } assert render_css(css) == """\
def test_override(): webkit, moz, ms, o, linear, gradient = Var.many( "webkit moz ms o linear gradient") css = { ".foo": { "background": override( -webkit - linear - gradient("left", "blue", "red", "blue"), -moz - linear - gradient("left", "blue", "red", "blue"), -ms - linear - gradient("left", "blue", "red", "blue"), -o - linear - gradient("left", "blue", "red", "blue"), linear - gradient("left", "blue", "red", "blue"), ) } } assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { background: -webkit-linear-gradient(left, blue, red, blue); background: -moz-linear-gradient(left, blue, red, blue); background: -ms-linear-gradient(left, blue, red, blue); background: -o-linear-gradient(left, blue, red, blue); background: linear-gradient(left, blue, red, blue); } """)
def test_defining_name_twice_in_same_scope(): css = { '%my-extend': { "margin": "1px" }, ".foo": { '%my-extend': { "margin": "1px" }, } } with pytest.raises(ValueError) as raised: render_css(css) assert str(raised.value) == "The extend `%my-extend` already exists"
def test_css_vars_decorator(): with tmp_load_keywords(): # globals are clean with pytest.raises(NameError): margin-bottom # noinspection PyUnresolvedReferences @css_vars(globals()) def css(): return {body: {margin-bottom: 3 * px, foo: none}} assert ( render_css(css()) == """\ body { margin-bottom: 3px; foo: none; } """ ) # foo and Foo where added to CSS_VARS assert len_css_vars() == LOADED_CSS_VARS_LEN + 2 # globals are cleared with pytest.raises(NameError): margin-bottom
def test_many_selectors_are_managed(): css = { ".foo": { "padding": "1px", (".bar", ".baz"): { # as tuple "padding": "none", "[data-foo], [data-bar]": { # as comma separated string (f"&:{x}" for x in ["hover", "focus"]): { # as generator "padding": "2px" } }, }, } } assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { padding: 1px; } .foo .bar, .foo .baz { padding: none; } .foo .bar [data-foo]:hover, .foo .bar [data-foo]:focus, .foo .bar [data-bar]:hover, .foo .bar [data-bar]:focus, .foo .baz [data-foo]:hover, .foo .baz [data-foo]:focus, .foo .baz [data-bar]:hover, .foo .baz [data-bar]:focus { padding: 2px; } """)
def test_implicit_many(): css = {".foo": {"font-family": ["Gill", "Helvetica", "sans-serif"]}} assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { font-family: Gill, Helvetica, sans-serif; } """)
def test_explicit_join(): css = {".foo": {"margin": join("1px", "2px", "3px", "4px")}} assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { margin: 1px 2px 3px 4px; } """)
def test_at_rule(): css = { ".foo": { "padding": "1px", media({"max-width": "1280px"}): { "padding": "2px", # direct properties "": { "padding": "2.1px" }, # or as empty string "&": { "padding": "2.2px" }, # or as "&" ".bar": { "padding": "3px" }, }, ".bar": { "padding": "4px", media({"min-width": "640px"}): { "padding": "5px", _: { "padding": "5.1px" }, # or as "_" (dummy var = empty string) }, }, } } assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { padding: 1px; } @media (max-width: 1280px) { .foo { padding: 2px; } .foo { padding: 2.1px; } .foo { padding: 2.2px; } .foo .bar { padding: 3px; } } .foo .bar { padding: 4px; } @media (min-width: 640px) { .foo .bar { padding: 5px; } .foo .bar { padding: 5.1px; } } """)
def test_declaration_without_value(): css = {"@charset 'UTF-8'": None, ".bar": {"padding": "1px"}} assert (render_css(css, mode=Modes.NORMAL) == """\ @charset 'UTF-8'; .bar { padding: 1px; } """)
def test_inner_combine(): ext1 = combine({"foo": 1}, {"foo": 11}) css = { "%ext2": combine({"bar": 2}, {"bar": 22}), "qux": extend(ext1, "ext2", css=combine({"baz": 3}, {"baz": 33})), "zzz": extend(ext1, "ext2", css=combine({"zzz": 4}, {"zzz": 44})) } assert render_css(css) == """\
def test_mode_indent2(): assert (render_css(css, Modes.INDENT2) == """\ /* comment */ .content { color: blue; font-weight: bold; background: green; } /* comment */ .content .foo { /* multi line comment */ color: green; } @media(all and (max-width: 600px) { /* comment */ .content { color: red; /* comment */ font-weight: normal; } .content .foo { color: yellow; } } .foo-bar {color: black} .content .bar { color: orange; } .content { z-index: 1; } /* comment */ .baz a { margin: 1px; } .baz b { margin: 2px; } """)
def test_implicit_ampersand_if_many_selectors(): css = {".foo": {"padding": "1px", ".bar, .baz": {"padding": "none"}}} assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { padding: 1px; } .foo .bar, .foo .baz { padding: none; } """)
def test_ampersand_repeated(): css = {".foo": {"padding": "1px", "&:hover, &:focus": {"padding": "none"}}} assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { padding: 1px; } .foo:hover, .foo:focus { padding: none; } """)
def test_key_implicit_nested(): css = {".foo": {"padding": "1px", ".bar": {"padding": "none"}}} assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { padding: 1px; } .foo .bar { padding: none; } """)
def test_mode_compact(): assert (render_css(css, Modes.COMPACT) == """\ .content {color: blue; font-weight: bold; background: green} .content .foo {color: green} @media(all and (max-width: 600px) { .content {color: red; font-weight: normal} .content .foo {color: yellow} } .foo-bar {color: black} .content .bar {color: orange} .content {z-index: 1} .baz a {margin: 1px} .baz b {margin: 2px} """)
def test_internal_dict(): css = { ".foo": { "margin": "2px" }, ".bar": extend({"margin": "1px"}), ".baz": { "a": { "margin": "3px" }, "b": extend({"margin": "1px"}, css={"margin": "4px"}), } } assert render_css(css) == """\
def test_implicit_many_and_join(): css = { ".foo": { "text-shadow": [ ("1px", "1px", "2px", "red"), (0, 0, "1em", "blue"), (0, 0, "0.2em", "blue"), ] } } assert (render_css(css, mode=Modes.NORMAL) == """\ .foo { text-shadow: 1px 1px 2px red, 0 0 1em blue, 0 0 0.2em blue; } """)
def test_not_used(): css = { ".foo": { "margin": "2px" }, '%my-extend': { "margin": "1px" }, ".baz": { "a": { "margin": "3px" }, } } assert render_css(css) == """\
def test_defining_name_twice_in_different_scopes(): css = { ".foo": { '%my-extend': { "margin": "1px" }, "a": extend("my-extend"), }, ".bar": { '%my-extend': { "margin": "2px" }, "a": extend("my-extend"), } } assert render_css(css) == """\
def test_extend_many(): ext1 = {"color": "ext1"} css = { ".foo": { "margin": "2px" }, "%ext2": { "color": "ext2" }, # all ext2 usages are here ".bar": extend(ext1), # all ext1 usages are here ".baz": { "a": { "margin": "3px" }, "b": extend(ext1, "ext2", css={"margin": "4px"}), } } assert render_css(css) == """\
def test_at_rule_at_first_level(): css = { "@media": { "should": "work", # invalid for media but valid for other @-rules "should-work": "too", ".foo": { "padding": "1px" }, } } assert (render_css(css, mode=Modes.NORMAL) == """\ @media { should: work; should-work: too; .foo { padding: 1px; } } """)
def test_with_combine(): lib = { '%my-extend': { "margin": "1px" }, } css = { ".foo": { "margin": "2px" }, ".bar": extend('my-extend'), ".baz": { "a": { "margin": "3px" }, "b": extend('my-extend', css={"margin": "4px"}), } } assert render_css(combine(lib, css)) == """\
def test_with_children(): my_extend = { "color": "red", "a": { "color": "blue", } } css = { ".foo": extend(my_extend, css={ "background": "white", "a": { "background": "yellow" } }), ".bar": extend(my_extend), } assert render_css(css) == """\
def test_chaining(): pad = {"padding": ".5em"} pad_box = extend(pad, css={ "a": { "text-decoration": "underline" }, "p, div": { "border": "solid black 1px", }, }) css = { "%message": extend(pad_box), "%message-important": extend("message", css={ "font-weight": "bold", "a": { "color": "red" } }), "%message-light": extend( "message", css={ # won't appear as not used "opacity": "0.8", }), ".message-error": extend("message-important"), ".message-warning": extend("message-important"), ".message-success, .message-info": extend("message", css={"a": { "color": "white" }}), } assert render_css(css) == """\
def render_accumulated_collected_to_string(self, acc: List) -> str: """Render the accumulated CSS parts to a string. All the parts are aggregated in a ``Combine``, that will convert strings to ``Raw``. This allow any parts to use "extends" defined previously. For the parameters, see ``Collector.render_accumulated_collected_to_string``. Returns ------- str The final CSS to display. """ final_list: List = [] for item in acc: if isinstance(item, Combine) or not callable(item): final_list.append(item) continue self.render_one_collected(item(), final_list) return get_default_mode().value["endline"] + render_css( combine(*final_list))
def test_combine(): css = combine({".foo": { "a": { "color": "blue" } }}, { ".bar": { "color": "white" }, ".foo": { "color": "red", "a": combine({"color": "yellow"}, { "text-decoration": "underline", "color": "pink", }) }, }) assert (render_css(css, mode=Modes.NORMAL) == """\ .foo a { color: blue; } .bar { color: white; } .foo { color: red; } .foo a { color: yellow; text-decoration: underline; color: pink; } """)