/**
 * QUnit Composite
 *
 * With custom modifications - all are marked with
 * a "Custom modification" comment.
 *
 * https://github.com/JamesMGreene/qunit-composite
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * https://jquery.org/license/
 */
(function( factory ) {
	if ( typeof define === "function" && define.amd ) {
		define( [ "qunit" ], factory );
	} else {
		factory( QUnit );
	}
}(function( QUnit ) {
var iframe, hasBound, resumeTests, suiteAssert,
	modules = 1,
	executingComposite = false;

function hasClass( elem, name ) {
	return ( " " + elem.className + " " ).indexOf( " " + name + " " ) > -1;
}

function addClass( elem, name ) {
	if ( !hasClass( elem, name ) ) {
		elem.className += ( elem.className ? " " : "" ) + name;
	}
}

function addEvent( elem, type, fn ) {
	if ( elem.addEventListener ) {
		// Standards-based browsers
		elem.addEventListener( type, fn, false );
	}
}

function runSuite( suite ) {
	var path;

	if ( QUnit.is( "object", suite ) ) {
		path = suite.path;
		suite = suite.name;
	} else {
		path = suite;
	}

	QUnit.test( suite, function( assert ) {
		resumeTests = assert.async();
		suiteAssert = assert;
		iframe.setAttribute( "src", path );
		// QUnit.start is called from the child iframe's QUnit.done hook.
	});
}

function initIframe() {
	var iframeWin,
		body = document.body;

	function onIframeLoad() {
		var moduleName, testName,
			count = 0;

		if ( !iframe.src ) {
			return;
		}

		// Deal with QUnit being loaded asynchronously via AMD
		if ( !iframeWin.QUnit && iframeWin.define && iframeWin.define.amd ) {
			return iframeWin.require( [ "qunit" ], onIframeLoad );
		}

		iframeWin.QUnit.moduleStart(function( data ) {
			// Capture module name for messages
			moduleName = data.name;
		});

		iframeWin.QUnit.testStart(function( data ) {
			// Capture test name for messages
			testName = data.name;
		});
		iframeWin.QUnit.testDone(function() {
			testName = undefined;
		});

		iframeWin.QUnit.log(function( data ) {
			if (testName === undefined) {
				return;
			}
			// Pass all test details through to the main page
			var message = ( moduleName ? moduleName + ": " : "" ) + testName + ": " + ( data.message || ( data.result ? "okay" : "failed" ) );
			suiteAssert.expect( ++count );
			suiteAssert.pushResult( {
				result: data.result,
				actual: data.actual,
				expected: data.expected,
				message: message
			} );
		});

		// Continue the outer test when the iframe's test is done
		iframeWin.QUnit.done(function() {
			resumeTests();
		});
	}

	iframe = document.createElement( "iframe" );
	iframe.className = "qunit-composite-suite";
	body.appendChild( iframe );

	addEvent( iframe, "load", onIframeLoad );

	iframeWin = iframe.contentWindow;
}

function appendSuitesToHeader( suites ) {
	var i, suitesLen, suite, path, name, suitesEl, testResultEl,
		newSuiteListItemEl, newSuiteLinkEl;

	suitesEl = document.getElementById("qunit-testsuites");

	if (!suitesEl) {
		testResultEl = document.getElementById("qunit-testresult");

		if (!testResultEl) {
			// QUnit has not been set up yet. Defer until QUnit is ready.
			QUnit.begin(function () {
				appendSuitesToHeader(suites);
			});
			return;
		}

		suitesEl = document.createElement("ul");
		suitesEl.id = "qunit-testsuites";
		testResultEl.parentNode.insertBefore(suitesEl, testResultEl);
	}

	for (i = 0, suitesLen = suites.length; i < suitesLen; ++i) {
		suite = suites[i];
		newSuiteLinkEl = document.createElement("a");
		newSuiteLinkEl.innerHTML = suite.name || suite;
		newSuiteLinkEl.href = suite.path || suite;

		newSuiteListItemEl = document.createElement("li");
		newSuiteListItemEl.appendChild(newSuiteLinkEl);

		suitesEl.appendChild(newSuiteListItemEl);
	}
}

/**
 * @param {string} [name] Module name to group these test suites.
 * @param {Array} suites List of suites where each suite
 *  may either be a string (path to the html test page),
 *  or an object with a path and name property.
 */
QUnit.testSuites = function( name, suites ) {
	var i, suitesLen;

	if ( arguments.length === 1 ) {
		suites = name;
		name = "Composition #" + modules++;
	}
	suitesLen = suites.length;

	appendSuitesToHeader(suites);

	if ( !hasBound ) {
		hasBound = true;
		QUnit.begin( initIframe );

		// TODO: Would be better to use something like QUnit.once( 'moduleDone' )
		// after the last test suite.
		QUnit.moduleDone( function () {
			executingComposite = false;
		} );

		QUnit.done(function() {
			iframe.style.display = "none";
		});
	}

	QUnit.module( name, {
		beforeEach: function () {
			executingComposite = true;
		}
	});

	for ( i = 0; i < suitesLen; i++ ) {
		runSuite( suites[ i ] );
	}
};

QUnit.testDone(function( data ) {
	if ( !executingComposite ) {
		return;
	}

	var i, len,
		testId = data.testId,
		current = document.getElementById( "qunit-test-output-" + testId ),
		children = current && current.children,
		src = iframe.src;

	if (!(current && children)) {
		return;
	}

	addEvent( current, "dblclick", function( e ) {
		var target = e && e.target ? e.target : window.event.srcElement;
		if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
			target = target.parentNode;
		}
		if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
			window.location = src;
		}
	});

	// Undo QUnit's auto-expansion for bad tests
	for ( i = 0, len = children.length; i < len; i++ ) {
		if ( children[ i ].nodeName.toLowerCase() === "ol" ) {
			addClass( children[ i ], "qunit-collapsed" );
		}
	}

	// Update Rerun link to point to the standalone test suite page
	current.getElementsByTagName( "a" )[ 0 ].href = src;
});

}));
