Inspecting CSS in Python with cssutils

For a new MyMart feature, we’re allowing our members to customise the look of parts of the site.They can choose custom colours with a palette widget. The values they give us are used to generate a CSS stylesheet which alters the rendering of certain page items.

I wanted to write test cases to ensure that the CSS is being generated correctly, so I used Christof Hoecke’s cssutils module to parse and then inspect the CSS.

cssutils parses a stylesheet into a hierarchy of DOM Level 2 objects. A stylesheet is typically made up of several CSSStyleRules which are broken down into a list of Selector objects and a CSSStyleDeclaration object which can be interrogated for the actual values we’re looking for.

First pull the CSS text into a variable, load it from file, enter it by hand, what ever is appropriate.

>>> pagecsstext = """div.data-key { color:#333333; }"""

Once we have the CSS in hand, parse it using cssutilsparseString method.

>>> from cssutils import parseString
>>> pagecss = parseString(pagecsstext)
>>> pagecss.cssRules
[<cssutils.css.cssstylerule.CSSStyleRule object at 0x1480230>]

For the tests, I then need to search out and evaluate specific rules and values. cssutils doesn’t make this as easy as we might like, my hope here was that I would be able to say something like pagecss.get_rule_for_selector("div.data-key") or something similar. There are problems with this for the implementor, I might request .data-key which should match in some situations. However, it would be nice to see a naive implementation added to the module. Here’s one I made earlier:

def get_rule_for_selector(stylesheet, selector):
    for rule in stylesheet.cssRules:
        if hasattr(rule, "selectorList") and selector in [s.selectorText for s in rule.selectorList]:
            return rule

We can then write expressions to get to the values of the stylesheet as follows.

>>> div_data_key = get_rule_for_selector(pagecss, "div.data-key")
>>> div_data_key = div_data_key.style.getPropertyValue("color") u'#333333'

Bringing it all together, here is my first test case. The page is served by Django, so I use their Client object to make a request.

class ShopTests(MyMartTestCase):
    def test_shop_css(self):
        c = Client()
        r = c.get("/somebody/screen.css")
        self.assertEqual(r.headers["Content-Type"], "text/css; charset=utf-8")

        rcss = parseString(r.content)

        tc_h2 = get_rule_for_selector(rcss, "table#content h2")
        self.assertEqual(tc_h2.style.getPropertyValue("background-color"),
                "#ff99cc")
        self.assertEqual(tc_h2.style.getPropertyValue("color"),
                "#000000")

        tc_body = get_rule_for_selector(rcss, "table#content div.block-body")
        self.assertEqual(tc_body.style.getPropertyValue("background-color"),
                "#ffccdd")
        self.assertEqual(tc_body.style.getPropertyValue("color"),
                "#333333")