phantomjs+qunitテストランナー

phantomjsからqunit実行したいなと思ったらphantomjsのexampleにあった。
https://github.com/ariya/phantomjs/blob/master/examples/run-qunit.js

これだとテスト数、成功数、失敗数しか出力されないのでもうすこし詳細がでるのがほしいなと思ってこのソースを参考に練習がてら自分で作ってみた。

var MAX_TIMEOUT = 3000;

function testCompleted() {
    var el = document.getElementById("qunit-testresult"),
        text = el.textContent;

    return (text && text.match("completed"));
}

function printResult() {
    var elem = document.getElementById("qunit-testresult"),
        passed = elem.querySelector(".passed").textContent;
        failed = elem.querySelector(".failed").textContent;
        total = elem.querySelector(".total").textContent;

    console.log("total: " + total + " passed: " + passed + " failed: " + failed);

    return (failed == 0);
}

function printResultDetail() {
    var slice = Array.prototype.slice,
        tests = slice.call(document.getElementById("qunit-tests").childNodes);


    tests.forEach(function (elem) {
        var moduleName = elem.querySelector(".module-name").textContent,
            testName = elem.querySelector(".test-name").textContent,
            passed = elem.querySelector(".passed").textContent,
            failed = elem.querySelector(".failed").textContent;

        console.log(moduleName + ": " + testName + "(" + passed + "," + failed + ")");

        lists = slice.call(elem.getElementsByTagName("ol").item(0).childNodes);
        lists.forEach(function (elem) {
            var msg, el;
            msg = "[" + elem.className + "]";
            if (elem.firstChild.nodeType == 3) {
                msg += elem.textContent;
            } else {
                if (el = elem.querySelector(".test-message")) {
                    msg += el.textContent;
                }
            }
            console.log("    " + msg);

            if (el = elem.querySelector(".test-expected")) {
                msg = el.getElementsByTagName("pre").item(0).textContent;
                console.log("        Expected: " + msg);
            }
            if (el = elem.querySelector(".test-actual")) {
                msg = el.getElementsByTagName("pre").item(0).textContent;
                console.log("        Result: " + msg);
            }
        });
    });
}

if (phantom.args.length === 0 || phantom.args.length > 2) {
    console.log("Usage: phantomjs-qunit-runner.js url [debug]");
    phantom.exit(0);
}

var url = phantom.args[0];
var isDebug = !!phantom.args[1];

var page = new WebPage();
page.onConsoleMessage = function (msg) {
    console.log(msg);
};

page.open(url, function (status) {
    if (status !== "success") {
        console.log("network error");
        phantom.exit(1);
    }
    var passed = false;
    var start = new Date().getTime();
    var timer = setInterval(function () {
        if (new Date().getTime() - start > MAX_TIMEOUT) {
            clearInterval(timer);
            console.log("timeout");
            phantom.exit(1);
        }
        if (page.evaluate(testCompleted)) {
            console.log("Test completed");
            passed = page.evaluate(printResult);
            if (isDebug) {
                page.evaluate(printResultDetail);
            }
            clearInterval(timer);
            if (passed) {
                phantom.exit(0);
            } else {
                phantom.exit(1);
            }
        }
        console.log("Test Running...");
    }, 100);
});

武士の家計簿をみつつダラダラ書いたのであんまり綺麗ではない。
パラメータで詳細を出すか出さないか制御できるようにしてみた。
詳細がでるようにするとこんな感じでモジュール名、テスト名、メッセージ、テスト結果とかを出力するようにした。

Test completed
total: 4 passed: 2 failed: 2
module1: test1(2,0)
    [pass]hoge
    [pass]piyo
        Expected: 1
module1: test2(0,2)
    [fail]hoge
    [fail]piyo
        Expected: [
  1,
  2,
  3
]
        Result: [
  1,
  2,
  4
]
module2: test3(0,0)

全部のassertの出力で試してないのでもしかしたら正しく値を拾えない場合があるかも。