4
\$\begingroup\$

I am in the process of writing tests for my JS code with CasperJS and Mocha. I have a function that changes the DOM and gets run on page load (through jQuery's document.ready event). I'm wondering what the best practice is for testing this kind of code. For example, I have a function removeElements that gets run before my tests start:

removeElements: function() {
    // Remove the original onsubmit attribute.
    $("#LoginForm").attr("onsubmit", "");
    $("#btnEnter").attr("onclick", "");

    // Modify form inputs.
    $("label[for=fieldPassword]").remove();

    $("#fieldPassword").remove();

    $("input[name=username][type=hidden]").remove();
}

Do you think I should only test that those elements have already been removed from the page from the execution of the code above? I am thinking that I should first test that those elements were there initially, call the function, then test that those elements and attributes were removed.

I suppose my real question is: what do I need to test, and what can I safely assume works without testing when it comes to DOM-altering code?

\$\endgroup\$
2
\$\begingroup\$

I think the real issue you are trying to solve is how to test code that has reach over the whole document. Just judging by your small snippet of code, you appear to be at least using "classes". Regardless of how you've written your application, you want to put yourself into this position when unit testing DOM manipulations:

describe("Something", function() {
    var div, something;

    beforeEach(function() {
        div = document.createElement("div");
        something = new Something(div);
    });

    it("modifies the DOM", function() {
        div.innerHTML = [
            '<form onsubmit="submit()">',
                '<label for="fieldPassword">Password</label>',
                '<input type="password" id="fieldPassword">',
                '<input type="hidden" name="username">',
                '<input type="submit" id="btnEnter" onclick="click()" value="Enter">',
            '</form>'
        ].join("");

        something.removeElements();

        expect(div.querySelector("form").onsubmit).toBe("");
        expect(div.querySelector("label")).toBe(null);
        expect(div.querySelector("input[type=password]")).toBe(null);
        expect(div.querySelector("input[type=hidden]")).toBe(null);
        expect(div.querySelector("input[type=submit]").onclick).toBe("");
    });
});

The code under test should have access to some sort of root element, which it then operates on. The beforeEach in your tests would create a new DIV tag, and a new instance of the object you want to test. This class should take the DIV tag as a constructor argument, ideally, but in the very least assign it as a property. Then your test is completely isolated from the rest of the DOM tree. There is no need to clean up these elements because they are all disconnected from the main document.

function Something(element) {
    this.element = $(element);
}

Something.prototype.removeElements = function() {
    this.element.find("#LoginForm").attr("onsubmit", "");
    this.element.find("#btnEnter").attr("onclick", "");

    // Modify form inputs.
    this.element.find("label[for=fieldPassword]").remove();

    this.element.find("#fieldPassword").remove();

    this.element.find("input[name=username][type=hidden]").remove();
};
\$\endgroup\$

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