Given-when-then testing

Proof of concept. Async sequential running of actions will allow for tests that interact with page loading, form submissions and maybe even ajax.

by andrewdavey

HTML

<input type="text" id="name"/><button id="go">Test</button>
<p id="message"></p>

JavaScript

// ******** CODE UNDER TEST ***********

$("#go").click(function() {
    $("#message").text("Hello, " + $("#name").val());
});


// ******** TEST CODE *************
initTestRunner();

// A passing test
given(
    loadPage("/")
).
when(
    type("#name", "Andrew"),
    click("#go")
).
then(
    $("#message").textShouldBe("Hello, Andrew")
);

// A failing test
given(
    loadPage("/")
).
when(
    type("#name", ""),
    click("#go")
).
then(
    $("#message").textShouldBe("Please enter your name.")
);


function initTestRunner() {
    $.fn.textShouldBe = function(value) {
        return function() {
            if (this.text() !== value) {
                throw new Error("Text should be '" + value + "' but was '" + this.text() + "'");
            }
        }.bind(this);
    };
    
    var actions = buildActions({
        click: function(id) {
            $(id).click();
        },
        type: function(id, text) {
            $(id).val(text);
        },
        loadPage: async(function(url) {
            var done = this.done;
            // FAKE CODE!
            // Real code will set and iframe's src to trigger page load
            // and call done once the window load event fires.
            setTimeout(function() {
                done();
            }, 100);
        }),
        pause: async(function(ms) {
            setTimeout(this.done, ms);
        }),
        message: function(text) {
            $("#message").text(text);
        }
    });
    $.extend(window, actions);
    
    window.given = (function() {
        return function () {
            var actions = copy(arguments);
            return {
                when: function() {
                    return when.apply({
                        actions: actions
                    }, arguments);
                }
            };
        }
    
        function when() {
            var actions = this.actions.concat(copy(arguments));
            return {
                then: function() {
                  ...