3

This may be a long shot, but suppose I have a requirement that a given section of a web page be bolded. That could be done with <b> or <strong> tags, or even a heading tag assuming that it kept the default styling of font-weight: bold. But suppose I also wanted to include cases where it matched a CSS-selector made the text bold. So, for example, in this case, the following cases would all pass:

<style>
.foo { font-weight: bold }
</style>

<h1>This should be bold</h1>
<p class="foo">This should be bold</p>
<strong>This should be bold</strong>

While the following test cases would fail:

<style>
h2 { font-weight: normal }
</style>

<h2>This should be bold</h2>
<p>This should be bold</p>

I'm ideally looking for a tool that can "render" the code programmatically and provide information about it programmatically, ideally from within Python or the command line.

Update

Following up on Todor's answer recommending Selenium with PhantomJS, I've come up with a solution that makes use of the Webdriver to render the HTML and then uses the .value_of_css_property method to check that the style is correct.

import os
import tempfile
from selenium import webdriver

class StylesTestMixin(object):
    def setUp(self):
        self.browser = webdriver.PhantomJS()

    def check_content_for_style(self, content, *args, **kwargs):
        with tempfile.NamedTemporaryFile(suffix='.html') as fo:
            fo.write(content.encode('utf-8'))
            fo.seek(0)
            self.check_url_for_style('file://{}'.format(fo.name), *args, **kwargs)

    def check_url_for_style(self, url, selector, property, expected, count=None, exact=False):
        self.browser.get(url)
        matching_els = self.browser.find_elements_by_css_selector(selector)
        if count and count != len(matching_els):
            raise AssertionError(
                "Expected {} matching elements, but found {}".format(
                    count, len(matching_els)
                )
            )
        for match in matching_els:
            actual = match.value_of_css_property(property)
            if exact: # property matches expected value exactly
                if actual != expected:
                    raise AssertionError(
                        "Expected {} with value {}, got {}\n\nElement: {}".format(
                            property, 
                            expected, 
                            actual, 
                            match.get_attribute('outerHTML')
                        )
                    )
            else: # only checks that property contains expected value and ignores case
                if expected.lower() not in actual.lower():
                    raise AssertionError(
                        "Expected {} containing value {}, got {}\n\nElement: {}".format(
                            property, 
                            expected, 
                            actual, 
                            match.get_attribute('outerHTML')
                        )
                    )

Then you can use it for testing like so:

from stylestest import StylesTestMixin
from unittest import TestCase

# normally this would be content generated in a function or something
SAMPLE_CONTENT = '''<html>
<head>
<title>Test Content</title>
<style type="text/css">
.foo { font-weight: bold; }
h2 { font-weight: normal; }
</style>
</head>
<body>
<h1 class="should-be-bold">This should be bold</h1>
<p class="foo should-be-bold">This should be bold</p>
<strong class="should-be-bold">This should be bold</strong>
<h2 class="shouldnt-be-bold">This should be bold</h2>
<p class="shouldnt-be-bold">This should be bold</p>
</body>
</html>'''


class TestingStyles(StylesTestMixin, TestCase):    
    def test_items_are_bold(self):
        self.check_content_for_style(
            SAMPLE_CONTENT,
            '.should-be-bold',
            'font-weight',
            'bold',
            count=3,
            exact=True
        )

    def test_items_arent_bold(self):
        self.check_content_for_style(
            SAMPLE_CONTENT,
            '.shouldnt-be-bold',
            'font-weight',
            'normal',
            count=2,
            exact=True
        )

    def test_items_are_italic(self):
        # this one is supposed to fail
        self.check_content_for_style(
            SAMPLE_CONTENT,
            'strong',
            'font-style',
            'italic',
        )
1
  • Try beautiful soup.
    – Yu Zhang
    Commented Mar 13, 2017 at 17:24

2 Answers 2

2

The most solid approach would be to render it, and see what's the style; and that's easier than it sounds :)

There's a Javascript method getComputedStyle which should do the heavy lifting for you - it returns literally what its name says, the element's styling after all css is applied.

A sample js code:

var elem = document.getElementById("myID");
var weight_value = window.getComputedStyle(elem, null).getPropertyValue("font-weight");

So wrap the page opening in Selenium, start a headless browser (phantomjs should be the easiest, FF in xvfb), execute the js and you'll have it.

Don't go down the road of parsing the html and the style sheets yourself - the possible combinations and caveats are mind-boggling (ex: are you sure you will handle the case where bold was set for your target element's class, but is explicitly overriden for that tag further in the css? Or for the tag, only when its parent is another specific tag?). That is the domain and the main purpose of the browsers' engines, leave it to them :)

1
  • Do you know why I could be getting this error message? NameError: name 'getComputedStyle' is not defined
    – mickael
    Commented Sep 26, 2019 at 19:30
1

You will need to write some custom code. There are multiple ways to do this depending on your end goal. There is a code only approach like what YuZhang suggests in utilizing a python library for pulling the info out of the page and checking it.

You could also pull the specifics with Selenium in the middle of a test with some tweaking. You pull the full html into the code and then search the nodes with xquery for the desired selections. Then you check certain paramaters.

Logic:

  1. Pull HTML out
  2. Retrieve CSS class names that specifically set font sizes to bold and put into a list
  3. Search your HTML for the specific elements you are trying to compare (likely need to check the full parent/child stack to ensure a parent doesn't have a css class defined and the child inherits it)
  4. Call a comparison method and pass the nodes and children in question of that along with the list of valid CSS class names. This method would compare to ensure one of the following exists (CSS class name, Inherited styles, inline styles).

Optionally you could do a step in your test and do the same thing with straight code too. If you want more specific help please dig into the details more on some of these points and ask a question related to that specifically.

Additionally if this is too cumbersome I'd recommend taking screenshots as you got through and then having a human look at the images to ensure bolded text is displayed. Takes a little human intervention, but the human eye/brain is exceptional at ultra fast usability recognition. If the screenshots are automated, it makes it simpler to just look and say yep, bolded or not...especially if you don't care what pattern the developer decided to style it with. The main goal I can see of what your after is ensuring consistent styling patterns are utilized and not inline styles...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.