<html>
<head>
<title>Fragment Mutation Tests</title>
<script>

if (window.testRunner)
    testRunner.dumpAsText();

var log = [];

function logResult(description, outcome)
{
    log.push({ description: description, outcome: outcome});
}

function printLog(methodName)
{
    var entries = log.map(function(entry) {
        return "<li>" + entry.description + ": " + entry.outcome;
    });
    document.body.appendChild(document.createElement("p")).innerHTML = "This test creates a fragment containing three elements: \"B\", \"U\", and \"P\", " +
        "  attempts to " + methodName + " this fragment and studies effects of mutation events on the fragment.";
    document.body.appendChild(document.createElement("ul")).innerHTML = entries.join("\n");
    document.body.appendChild(document.createElement("br"));
    log = [];
}

function produceNodeNameString(nodes)
{
    var node = nodes.firstChild;
    var result = "";
    while(node) {
        result += node.nodeName;
        node = node.nextSibling;
    }
    return result;
}

function expectTypeError()
{
    return function(stash, exception) {
        if (!exception)
            return "FAIL, expected TypeError. The resulting fragment was: \"" + produceNodeNameString(stash) + "\".";

        if (exception.name = "TypeError")
            return "PASS";
        return "FAIL, expected TypeError, was: " + exception.name + ".";
    }
}

function expectNodes(nodes)
{
    return function(stash, exception) {
        if (exception)
            return "FAIL, unexpected exception thrown: " + exception;
        var result = produceNodeNameString(stash);
        if (nodes == result)
            return "PASS";
        return "FAIL, expected \"" + nodes + "\", was \"" + result + "\".";
    };
}

function testFragment(method, description, mutationHandler, expectation, nonStop)
{
    var once = 0;
    var logged = 0;
    var frag = document.createDocumentFragment();
    var stash = document.body.appendChild(document.createElement("div"));
        frag.appendChild(document.createElement("b"));
        frag.appendChild(document.createElement("u"));
        frag.appendChild(document.createElement("p"));
        frag.addEventListener("DOMSubtreeModified", function(evt)
        {
            if (!nonStop && once++)
               return;

            try {
                mutationHandler(evt, frag, stash);
            }
            catch(e) {
                logResult(description, expectation(stash, e));
                logged++;
            }
        }, false);

    try {
        method(stash, frag);
    }
    catch(e) {
        logResult(description, expectation(stash, e));
        logged++;
    }
    if (!logged)
        logResult(description, expectation(stash));
    document.body.removeChild(stash);
}

function appendChildMethod(object, subject)
{
    object.appendChild(subject);
}

function insertBeforeMethod(object, subject)
{
    object.insertBefore(subject, object.firstChild);
}

function runTest(methodName, method)
{
    var missing = document.body.appendChild(document.createElement("em"));
    testFragment(method, "Inserting an element in front of the next item in fragment should not affect the result", function(evt, frag)
    {
        frag.insertBefore(missing, frag.firstChild);
    }, expectNodes("BUP"));

    var extra = document.body.appendChild(document.createElement("em"));
    testFragment(method, "Appending an element at the end of the fragment should not affect the result", function(evt, frag)
    {
        frag.appendChild(extra);
    }, expectNodes("BUP"));

    testFragment(method, "Continually re-appending removed element to the fragment should eventually throw a TypeError", function(evt, frag, stash)
    {
        stash.insertBefore(frag.lastChild, stash.firstChild);
    }, expectTypeError(), true);
    printLog(methodName);
}
function runTests()
{
    runTest("appendChild", appendChildMethod);
    runTest("insertBefore", insertBeforeMethod);
}

</script>
</head>
<body onload="runTests()">
</body>
</html>
