Tag Archive for 'css'

Unit testing for placeholder-based view-helpers in Zend Framework

Just got bitten by an interesting little issue.

I have a Zend Framework view-helper that I use to add a CSS class to an HTML <body> tag. Usage in a view-script is:

$this->bodyTag()->addClass('someclass');

Then in a layout, I’d output it using:

<?= $this->bodyTag() ?>

The class itself is very simple:


/**
 * A body tag
 *
 * @author David Weinraub <david@papayasoft.com>
 */
class PapayaSoft_Zend_View_Helper_BodyTag extends Zend_View_Helper_Placeholder_Container_Standalone
{
    public function bodyTag()
    {
        return $this;
    }

    public function toString()
    {
        $tag = array();
        $tag[] = '<body';

        $storage = $this->getContainer();

        if (count($storage) > 0){
            $classes = array();
            foreach ($storage as $class){
                $classes[] = $class;
            }
            $tag[] = sprintf(' class="%s"', implode(' ', $classes));
        }
        $tag[] = '>';
        return implode('', $tag);
    }

    public function addClass($class)
    {
        $this->getContainer()->append($class);
        return $this;
    }
}

I’ve begun moving this kind of stuff out into my own library, which I will manage in its own project under source control. I can then import it into multiple projects, just as I would with the libs for Zend or Doctrine or HTML Purifier.

Consequently, it’s now time – actually long past-due – to really get into the discipline of unit-testing. A simple class like this seems a decent place to start.

Unit-tests for the BodyTag class looked something like this:


/**
 * Test BodyTag class
 *
 * @group Zend_View_Helper
 * @author David Weinraub <david@papayasoft.com>
 */
class PapayaSoft_Zend_View_Helper_BodyTagTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var PapayaSoft_Zend_View_Helper_BodyTag
     */
    private $_bodyTag;

    public function setUp()
    {
        $this->_bodyTag = new PapayaSoft_Zend_View_Helper_BodyTag();
    }

    public function tearDown()
    {
        unset($this->_bodyTag);
    }

    public function testRenderOnNoAddedClasses()
    {
        $this->assertEquals('<body>', (string) $this->_bodyTag);
    }

    public function testRenderOnSingleAddedClass()
    {
        $this->_bodyTag->addClass('myclass');
        $this->assertEquals('<body class="myclass">', (string) $this->_bodyTag);
    }
    
    public function testRenderOnMultipleAddedClasses()
    {
        $this->_bodyTag = new PapayaSoft_Zend_View_Helper_BodyTag();
        $this->_bodyTag->addClass('someclass')->addClass('anotherclass');
        $this->assertEquals('<body class="someclass anotherclass"<', (string) $this->_bodyTag);
    }
}

Pretty straightforward. Or so I thought.

The last test failed, reporting the following:

BodyTagTest::testRenderOnMultipleAddedClasses()
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-<body class="someclass anotherclass">
+<body class="myclass someclass anotherclass">
It seemed as if result of running the penultimate method testRenderOnSingleAddedClass() was still in effect when testRenderOnMultipleAddedClasses() started. When the testRenderOnSingleAddedClass() method is removed, the testRenderOnMultipleAddedClasses() passes.

What’s going on here? The whole point of tearDown() and setUp() is to reset the test environment to a clean state, in this case by unsetting and re-instantiating the $_bodyTag member variable.

Well, the whole key here turns out to be state of the system. It’s not sufficient to simply reset the object in the member variable if it has made other changes to the state of the system. Sort of like selling the cow that crapped in your living room, but forgetting to clean up the mess he made. And that’s actually what appears to be happening.

The framework’s own unit test for Zend_View_Helper_HeadTitle, another Zend_View_Helper_Placeholder_Container_Standalone-extended class, offers some hint of this. The setUp() method is as follows:

    /**
     * Sets up the fixture, for example, open a network connection.
     * This method is called before a test is executed.
     *
     * @return void
     */
    public function setUp()
    {
        $regKey = Zend_View_Helper_Placeholder_Registry::REGISTRY_KEY;
        if (Zend_Registry::isRegistered($regKey)) {
            $registry = Zend_Registry::getInstance();
            unset($registry[$regKey]);
        }
        $this->basePath = dirname(__FILE__) . '/_files/modules';
        $this->helper = new Zend_View_Helper_HeadTitle();
    }

So there is some Zend_Registry stuff in there that needs to be cleared, as well. Adding the registry-clearing code from the ZF unit-test into my own solved the problem.

Live and learn.

HTML email templates from CampaignMonitor: Fail?

Clients love HTML email.

Done correctly, it looks slick as all get-out; it prompts users to action; it drives traffic for a special promotion; presents an exciting, professional image; lifts you aloft and floats you to heaven. All with higher response/action rates than plain-text email messages.

However, as every web developer knows, doing it well is a nightmare. Design/layout is the easy part; the sticky parts are numerous. Different email clients render HTML email with, shall we say, somewhat uneven support for web standards that are increasingly respected in modern browsers. Further, many email clients come configured with default settings to that disable images, adding another factor to consider. Finally – well, not finally, but the list of issues feels effectively endless – HTML email is often used by spammers, so there are more complicated considerations associated to ensuring that your messages do not get incorrectly tagged as spam.

There is a growing body of best-practices that has slowly developed for HTML email. These practices are not “best” in the sense that they represent elegant, flexible coding philosophies (like DRY, OO, for example) or minimal load times or optimized development practices. Rather, these ideas are considered best-practices, IMHO, merely in the sense that they “work”: they offer the best chance that your message will be received by the intended recipient and be reasonably well-rendered when he views it.

These best-practices include foregoing all the techniques we learned for creating semantic markup that cleanly separates content from presentation using CSS; instead, we are told to use tables for layout. We should no longer refer to styles in an external style sheet since most email clients – especially web-based ones like Hotmail, Yahoo, and Gmail – will strip them out; rather, we should use inline styling, repeating style information on nearly every element that needs it, inducing bloat and making modifications a tedious search-and-replace activity rather than a simple change in a single style declaration.

Other best-practice resources often conclude by pointing to Campaign Monitor or MailChimp and advising readers to download their free templates and use them as directly or as a starting point for editing or even as a guide to these best-practices. Both sites claim that their templates have been tested to render fine in all major clients.

Now, Campaign Monitor is great. These guys are founders of the Email Standards Project. I totally support the idea that someone should be pushing email clients towards better support of web standards, especially CSS support. The first step in that direction is measuring and evaluating the current support. And their chart of CSS support in major mail clients is a tremendous resource.

But the first template I downloaded from CampaignMonitor violates a bunch of these best practices right off the bat and fails to render correctly in Gmail. The template has nearly all of its styling inside a <style> tag inside the page’s <head> section. CampaignMonitor’s own CSS support guide referenced above notes that such an approach will not work with Gmail, yet their template includes it and claims to render fine.

Gotta tell ya, I’m a bit disappointed.