// ==UserScript==
// @name           Selenium RC HTML driver via GreaseMonkey
// @namespace      dtakamori.com
// @include        http://localhost/selenium/*.html
// ==/UserScript==

var host = "localhost";
var port = "4444";
var browser = "*opera";
var verbose = false;

var base_url;
var links = document.getElementsByTagName("link");
if (links.length) {
	base_url = links[0].getAttribute("href");
}

var params = {};
if (location.search && location.search.length > 0) {
	var queryParams = location.search.substring(1).split("&");
	for (var i in queryParams) {
		var eqIndex = queryParams[i].indexOf('=');
		if (eqIndex != -1) {
			var param = queryParams[i].substring(0, eqIndex);
			var value = queryParams[i].substring(eqIndex + 1);
			params[param] = value;
		}
	}
}
if (params.baseurl) base_url = params.baseurl;
if (params.browser) browser = params.browser;
if (params.host) host = params.host;
if (params.port) host = params.port;
if (params.verbose) verbose = true;
var selenium_server = "http://" + host + ":" + port;

function log(msg) {
	if (unsafeWindow.console) {
		unsafeWindow.console.log(msg);
	} else {
		var logContainer = document.getElementById("log");
		if (logContainer) {
			logContainer.innerHTML += msg + "\n";
		} else {
			alert(msg);
		}
	}
}

function execute(cmd, args, options) {
	var INDEX_SPECIFIER = "index=";
	var ID_SPECIFIER = "id=";
	var LABEL_SPECIFIER = "label=";
	var VALUE_SPECIFIER = "value=";

	if (cmd == "store") {
		cmd += "Expression";
	} else if (cmd == "assertSelected" || cmd == "verifySelected") {
		if (args[1].indexOf(INDEX_SPECIFIER) == 0) {
			cmd += "Index";
			args[1] = args[1].substring(INDEX_SPECIFIER.length);
		} else if (args[1].indexOf(ID_SPECIFIER) == 0) {
			cmd += "Id";
			args[1] = args[1].substring(ID_SPECIFIER.length);
		} else if (args[1].indexOf(LABEL_SPECIFIER) == 0) {
			cmd += "Label";
			args[1] = args[1].substring(LABEL_SPECIFIER.length);
		} else if (args[1].indexOf(VALUE_SPECIFIER) == 0) {
			cmd += "Value";
			args[1] = args[1].substring(VALUE_SPECIFIER.length);
		} else {
			cmd += "Label";
		}
	} else if (cmd.substring(cmd.length - 11) == "ErrorOnNext" || cmd.substring(cmd.length - 13) == "FailureOnNext") {
		//expectError = true;
		//return "OK";
	} else if (cmd == "echo") {
		//return "OK," + args[0];
	} else if (cmd == "pause") {
/*
		try {
			Thread.sleep(Integer.parseInt(args[0]));
			return "OK";
		} catch (InterruptedException e) {
			return "ERROR: pause interrupted";
		}
*/
	}
	var url = selenium_server + "/selenium-server/driver/?cmd=" + cmd + 
		"&1=" + (args[0] ? encodeURIComponent(args[0]) : "") + 
		"&2=" + (args[1] ? encodeURIComponent(args[1]) : "");
	if (options.sessionId) {
		url += "&sessionId=" + options.sessionId;
	}
	if (verbose) {
		log("url=" + url);
	}
	GM_xmlhttpRequest({
		method: "GET",
		url: url,
		onload: function(responseDetails) {
			var response = responseDetails.responseText;
			if (response.substring(0, 2) == "OK") {
				if (options.onComplete) options.onComplete(response);
			} else {
				if (options.onError) options.onError(response);
			}
		},
		onerror: function(responseDetails) {
			if (options.onError) {
				options.onError("Received error HTTP " + 
					responseDetails.status +
					" " + responseDetails.statusText);
			}
		}
	});
}

function executeSteps(args) {
	if (args.steps.length == 0) {
		if (args.onComplete) args.onComplete();
		return;
	}
	var stepsCopy = args.steps.concat();
	var step = stepsCopy.shift();
	var doNext = function() {
		executeSteps({
			steps: stepsCopy, 
			sessionId: args.sessionId, 
			onStep: args.onStep,
			onStepComplete: args.onStepComplete,
			onStepError: args.onStepError,
			onComplete: args.onComplete, 
			onError: args.onError
		});
	};
	var onComplete = function(result) {
		step.result = result;
		if (args.onStepComplete) args.onStepComplete(step, result);
		doNext();
	};
	var onError = function(error) {
		step.result = error;
		if (args.onStepError) args.onStepError(step, error);
		if (step.cmd.indexOf("verify") != 0) {
			if (args.onError) args.onError(error);
		} else {
			doNext();
		}
	}
	if (args.onStep) {
		args.onStep(step);
	}
	execute(step.cmd, [step.arg1, step.arg2], {
		sessionId: args.sessionId, 
		onComplete: onComplete, 
		onError: onError
	});
}

function runTest(browser, base_url, onComplete, onError) {
	var head = document.getElementsByTagName("head")[0];
	head.innerHTML += "<style>" + 
		".ran { background-color: #eeffee; }" +
		".passed { background-color: #ccffcc; }" +
		".failed { background-color: #ffcccc; }" +
		".error { background-color: #ffeeee; }" +
		"</style>";
	var body = document.getElementsByTagName("body")[0];

	var resultContainer = document.createElement("h3");
	resultContainer.innerHTML = "Result: ";
	body.insertBefore(resultContainer, body.firstChild);
	var result = document.createElement("span");
	result.setAttribute("id", "result");
	resultContainer.appendChild(result);

	var stepRows = document.getElementsByTagName("tr");
	var headerRow = stepRows[0];
	headerRow.insertCell(1).innerHTML="Result";
	var steps = new Array();
	for (var i = 1; i < stepRows.length; i++) {
		var stepFields = stepRows[i].getElementsByTagName("td");
		if (stepFields.length == 1) {
			continue;
		}
		var fields = new Array();
		for (var j = 0; j < stepFields.length; j++) {
			var field = stepFields[j].textContent;
    field = field.replace(/\r\n|\r/g, "\n");
    field = field.replace(/\ +/g, " ");
    var nbspPattern = new RegExp(String.fromCharCode(160), "g");
    field = field.replace(nbspPattern, " ");
    var result = field.replace(/^\s+/g, "");
    field = result.replace(/\s+$/g, "");
			//if (arg == "&nbsp;") arg = "";
			fields.push(field);
		}
		steps.push({
			elt: stepRows[i],
			cmd: fields[0],
			arg1: fields[1],
			arg2: fields[2]
		});
	}
	var testFailure = false;

	execute("getNewBrowserSession", [browser, base_url],
		{
		onComplete: function(result) {
			var sessionId;
			if (result.length > 2) {
				sessionId = result.substring(3);
			}
			executeSteps({
				steps: steps, 
				sessionId: sessionId, 
				onStep: function(step) {
					var cell = step.elt.insertCell(3);
					cell.innerHTML = "Running";
				},
				onStepComplete: function(step, result) {
					step.elt.cells[3].innerHTML = result;
					step.elt.setAttribute("class", "passed");
				},
				onStepError: function(step, error) {
					step.elt.cells[3].innerHTML = error;
					step.elt.setAttribute("class", "failed");
					testFailure = true;
				},
				onComplete: function() {
					execute("testComplete", [], { sessionId: sessionId });
					if (testFailure) {
						headerRow.setAttribute("class", "failed");
						document.getElementById("result").innerHTML = "FAILED";
						document.getElementById("result").setAttribute("class", "failed");
					} else {
						headerRow.setAttribute("class", "passed");
						document.getElementById("result").innerHTML = "PASSED";
						document.getElementById("result").setAttribute("class", "passed");
					}
					log("Test Complete");
					if (onComplete) onComplete();
				}, 
				onError: function(error) {
					execute("testComplete", [], { sessionId: sessionId });
					headerRow.setAttribute("class", "failed");
					document.getElementById("result").innerHTML = "FAILED";
					document.getElementById("result").setAttribute("class", "failed");
					log(error);
					if (onError) onError(error);
				}
			});
		}, 
		onError: function(error) {
			document.getElementById("result").innerHTML = "ERROR";
			log("Failed to initialize session");
			log(error);
			if (onError) onError("Error on Initialization: " + error);
		}
	});
}

try {
	if (!unsafeWindow.console) {
		var body = document.getElementsByTagName("body")[0];
		var executionLogContainer = document.createElement("div");
		executionLogContainer.innerHTML = "Execution Log:";
		body.appendChild(executionLogContainer);
		var executionLog = document.createElement("div");
		executionLog.setAttribute("id", "log");
		executionLog.setAttribute("style", "white-space: pre;");
		executionLogContainer.appendChild(executionLog);
	}
	runTest(browser, base_url);
} catch (e) {
	log("Exception caught");
	log(e);
}

