When Stringifiying a DOM Node Gives You Too Much

I’ve been refactoring an internal application that we use extensively at my organization. It’s called Sanity.

It is a general-purpose HTML quality checker that’s ostensibly for testing accessibility, but it’s general enough that it can test all kinds of things above and beyond the live-DOM including:

    • All assets loaded by the page return nonĀ 4xx or 5xx HTTP status codes.
    • No mixed content (our https pages don’t load http resources)
    • JavaScript coverage (what percentage of the JavaScript loaded and parsed is actually being executed).

Sanity is a thin command-line interface over an ES6 class-based API, which loads plain, vanilla JavaScript tests in headless Chrome. It leverages Puppeteer (a Node.js API wrapper for Chrome).

The test-runner emits events which reporter classes can listen to. For each URL tested, the StringReporter class reports the results of the tests to stdout. Let’s say we are testing that nodes expressing heading semantics should use one of the HTML heading tags instead of something awful like this:

<p class="heading-2">My Heading</p>

Yuck. It should be written like this:

<h2>My Heading</h2>

When this test fails, we want to report that it fails, report the reason, and provide the stringified DOM node in question so that users can find the problem in their HTML. If we do this:

...
node.toString()

we get something like this:

[object HTMLHeadingElement]

…not what we want.

Instead we want the outerHTML of the DOM node. Of course if the node has a lot of children we’ll get back reams of HTML.

So instead, let’s truncate the return value. Something like this will do:

/**
 * Stringifies a DOM node.
 * @param {Object} el - A DOM node.
 * @param {Number} truncate - How much to truncate 
 * .                          outerHTML of element.
 * @returns {String} - A stringified node with attributes
 *                     retained.
 */
function stringifyEl(el, truncate) {
    var truncateLen = truncate || 50;
    var outerHTML = el.outerHTML;
    var ret = outerHTML;
    ret = ret.substring(0, truncateLen);

    // If we've truncated, add an elipsis.
    if (outerHTML.length > truncateLen) {
      ret += "...";
    }
    return ret;
}

Here’s the gist.

The next step would be to also report the line number. I think we could do that by climbing up the DOM to the html node, getting the outerHTML, then splitting on new lines. That wouldn’t account for the DOCTYPE line, but it would get us most of the way there. Maybe I’ll do that next…

…on second thought, that would be pretty useless because the live DOM, in most cases, will be quite different from the actual source HTML It’s probably not worth the time, at least at this point.

Leave a Reply

Your email address will not be published. Required fields are marked *