Archive

javascript test driven development with qunit

I have been getting deep into TDD lately, and qUnit became the framework of my choice. This is how I decided: I went to John Resing's site and looked what he uses :) (actually wrote/co-wrote it).

Anyway, I love TDD now. It is a lot of work, and write a good test is harder that writing a good code, but it will make you much better developer who writes much better code.

I have written a simple loader which allows you to load qUnit from an external file, rather than having to have to include the HTML on the page, which is nice in combination with some client side or server side mechanism that will include your tests with dev - exclude in production.

(function ($) {

    //test case init

    var stringBuilder = function () {

        var s = [];
        return {
            // appends
            append: function (v) {
                if (v) {
                    s.push(v);
                }
            },
            // clears
            clear: function () {
                s.length = 1;
            },
            // converts to string
            toString: function () {
                return s.join("");
            }
        }
    };

    var sb = stringBuilder();
    sb.append("<div style=\"position:absolute; right:0%; width:350px;\">");
    sb.append("<h1 id=\"qunit-header\">QUnit Test Suite</h1>");
    sb.append("<h2 id=\"qunit-banner\"></h2>");
    sb.append("<div id=\"qunit-testrunner-toolbar\"></div>");
    sb.append("<h2 id=\"qunit-userAgent\"></h2>");
    sb.append("<ol id=\"qunit-tests\"></ol>");
    sb.append("<div id=\"qunit-fixture\">test markup</div>");
    sb.append("</div>");
    $("body").append(sb.toString());

})(jQuery);

Here is how I like to write my tests: despite of all of the methods being available as globals, this might be dangerous in a complex app, so I wrap the code in a closure, and use the namespaced methods which are available under the qunit namespace:

/*test 1*/
(function (T, $) {

    //get elements
    var doc = $(document);

    T.module("First Test");

    T.test("Toggle", function () {

        //how many tests are expected to pass:
        expect(2);

        T.equals("block", "block");

        T.equals("none", "none");

    });

})(QUnit, jQuery);

To get some ideas how to write tests, there are some great test cases written as examples on the bottom of the qunit page showing how jquery itself is being tested.

I have seen this quote somewhere recently: "Debugging sucks, TDD rocks!"

Russian UI Developers Rock!

So I have this new friend at my new job and he was introducing me today the russian UI developer community. This made me realize, there is some serious talent in the formal Soviet Union. He explained to me that there were too many unemployed PhDs desperate to find work, and given the fact that UI development is a combination of science, hacking and art, this fits the Russian culture very well. Combine this with some a lot of time and bright brains, and you got the perfect environment for "browser innovation."

Sergey (Siarhei) mentioned these two websites to be his favorite sources of inspiration:
http://habrahabr.ru/ (Каскадные Таблицы Стилей) http://chikuyonok.ru/

So if you get get through the "azbuka" with your google translator, you might find some innovative ways to make your browser do things you did not know it was capable of.

jQuery Star Ratings Plugin

I needed a very simple jQuery Star Ratings Plugin, and what I found available out there just seemed too cluttered and complicated, so I came up with my own. The idea was to create something with the small amount of code possible, that would have two features: show rating, and allow for adding star rating to any type of form simply taking over a input filed

So here is what I came up with in a few hours:

(function ($) {

        $.fn.stars = function (op) {

            var starHtml = "<div><ul class=\"stars clearfix\"><li><a href=\"#1\" class=\"star_1\" rating=\"1\"><span class=\"selected\"></span></a></li><li><a href=\"#2\" class=\"star_2\" rating=\"2\"><span class=\"selected\"></span></a></li><li><a href=\"#3\" class=\"star_3\" rating=\"3\"><span class=\"selected\"></span></a></li><li><a href=\"#4\" class=\"star_4\" rating=\"4\"><span></span></a></li><li><a href=\"#5\" class=\"star_5\" rating=\"5\"><span></span></a></li></ul></div>";

            return this.each(function () {

                var wrap = $(this),
                    html = $(starHtml),
                    d = $(".dd", html),
                    a = $("a", html),
                    currentRating = wrap.attr("rating"),
                    isReadOnly = true;

                if (typeof currentRating == "undefined") {
                    currentRating = "0";
                    isReadOnly = false;
                }

                //rating
                html.addClass("stars_" + currentRating);

                //bind events if is not read only
                if (isReadOnly) {

                    //return false on click
                    a.click(function () {
                        return false;
                    });

                    //kills link default cursor
                    a.css("cursor", "default");

                }
                else {

                    //add default rating (0)
                    wrap.val(currentRating);

                    //click
                    a.click(function () {

                        var _this = $(this),
                        r = _this.attr("rating");

                        //add value
                        wrap.val(r);

                        //remove class
                        var currentClass = html.attr("class");
                        html.removeClass(currentClass);

                        //add current class
                        html.addClass("stars_" + r);

                        //update current
                        currentRating = r;

                        return false;

                    });

                    //hover
                    a.hover(function () {
                        var _this = $(this),
                        r = _this.attr("rating");

                        //add value
                        wrap.val(r);

                        //remove class
                        var currentClass = html.attr("class");
                        html.removeClass(currentClass);

                        //add current class
                        html.addClass("hover_" + r);

                        return false;
                    },
                    function () {

                        var currentClass = html.attr("class");
                        html.removeClass(currentClass);

                        //add current class
                        html.addClass("stars_" + currentRating);

                        currentRating

                        return false;
                    });

                }


                //put on page
                wrap.after(html).hide();

            });

        };


    })(jQuery);

And this is how you can use this plugin:

<p>Display Stars</p>

<span class="star_rating" rating="1"></span>

<span class="star_rating" rating="2"></span>

<span class="star_rating" rating="3"></span>

<span class="star_rating" rating="4"></span>

<span class="star_rating" rating="5"></span>

<p>Rate With Stars</p>
<input name="rating" value="0" class="star_rating" />

And here is how you can initiate it:


$(".star_rating").stars();

It works great for blog post comments, or simple directories, as this is where I am using it

You can download the source code of the star jquery plugin here.