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',
)