/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
passColor = "#cfffcf";
failColor = "#ffcfcf";
workingColor = "#DEE7EC";
// The current row in the list of commands (test script)
currentCommandRow = 0;
inputTableRows = null;
// The current row in the list of tests (test suite)
currentTestRow = 0;
// Whether or not the jsFT should run all tests in the suite
runAllTests = false;
// Whether or not the current test has any errors;
testFailed = false;
suiteFailed = false;
// Holds the handlers for each command.
commandHandlers = null;
// The number of tests run
numTestPasses = 0;
// The number of tests that have failed
numTestFailures = 0;
// The number of commands which have passed
numCommandPasses = 0;
// The number of commands which have failed
numCommandFailures = 0;
// The number of commands which have caused errors (element not found)
numCommandErrors = 0;
// The time that the test was started.
startTime = null;
// The current time.
currentTime = null;
// An simple enum for failureType
ERROR = 0;
FAILURE = 1;
runInterval = 0;
function setRunInterval() {
// Get the value of the checked runMode option.
// There should be a way of getting the value of the "group", but I don't know how.
var runModeOptions = document.forms['controlPanel'].runMode;
for (var i = 0; i < runModeOptions.length; i++) {
if (runModeOptions[i].checked) {
runInterval = runModeOptions[i].value;
break;
}
}
}
function continueCurrentTest() {
document.getElementById('continueTest').disabled = true;
testLoop.finishCommandExecution();
}
function getApplicationFrame() {
return document.getElementById('myiframe');
}
function getSuiteFrame() {
return document.getElementById('testSuiteFrame');
}
function getTestFrame(){
return document.getElementById('testFrame');
}
function loadAndRunIfAuto() {
loadSuiteFrame();
}
function start() {
setRunInterval();
loadSuiteFrame();
}
function loadSuiteFrame() {
var testAppFrame = document.getElementById('myiframe');
selenium = Selenium.createForFrame(testAppFrame);
registerCommandHandlers();
//set the runInterval if there is a queryParameter for it
var tempRunInterval = getQueryParameter("runInterval");
if (tempRunInterval) {
runInterval = tempRunInterval;
}
document.getElementById("modeRun").onclick = setRunInterval;
document.getElementById('modeWalk').onclick = setRunInterval;
document.getElementById('modeStep').onclick = setRunInterval;
document.getElementById('continueTest').onclick = continueCurrentTest;
var testSuiteName = getQueryParameter("test");
if (testSuiteName) {
addLoadListener(getSuiteFrame(), onloadTestSuite);
getSuiteFrame().src = testSuiteName;
} else {
onloadTestSuite();
}
}
function startSingleTest() {
removeLoadListener(getApplicationFrame(), startSingleTest);
var singleTestName = getQueryParameter("singletest");
addLoadListener(getTestFrame(), startTest);
getTestFrame().src = singleTestName;
}
function getIframeDocument(iframe)
{
if (iframe.contentDocument) {
return iframe.contentDocument;
}
else {
return iframe.contentWindow.document;
}
}
function onloadTestSuite() {
removeLoadListener(getSuiteFrame(), onloadTestSuite);
suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0];
// Add an onclick function to each link in the suite table
for(rowNum = 1;rowNum < suiteTable.rows.length; rowNum++) {
addOnclick(suiteTable, rowNum);
}
if (isAutomatedRun()) {
startTestSuite();
} else if (getQueryParameter("autoURL")) {
addLoadListener(getApplicationFrame(), startSingleTest);
getApplicationFrame().src = getQueryParameter("autoURL");
} else {
testLink = suiteTable.rows[currentTestRow+1].cells[0].getElementsByTagName("a")[0];
getTestFrame().src = testLink.href;
}
}
// Adds an onclick function to the link in the given row in suite table.
// This function checks whether the test has already been run and the data is
// stored. If the data is stored, it sets the test frame to be the stored data.
// Otherwise, it loads the fresh page.
function addOnclick(suiteTable, rowNum) {
aLink = suiteTable.rows[rowNum].cells[0].getElementsByTagName("a")[0];
aLink.onclick = function(eventObj) {
srcObj = null;
// For mozilla-like browsers
if(eventObj)
srcObj = eventObj.target;
// For IE-like browsers
else if (getSuiteFrame().contentWindow.event)
srcObj = getSuiteFrame().contentWindow.event.srcElement;
// The target row (the event source is not consistently reported by browsers)
row = srcObj.parentNode.parentNode.rowIndex || srcObj.parentNode.parentNode.parentNode.rowIndex;
// If the row has a stored results table, use that
if(suiteTable.rows[row].cells[1]) {
var bodyElement = getIframeDocument(getTestFrame()).body;
// Create a div element to hold the results table.
var tableNode = getIframeDocument(getTestFrame()).createElement("div");
var resultsCell = suiteTable.rows[row].cells[1];
tableNode.innerHTML = resultsCell.innerHTML;
// Append this text node, and remove all the preceding nodes.
bodyElement.appendChild(tableNode);
while (bodyElement.firstChild != bodyElement.lastChild) {
bodyElement.removeChild(bodyElement.firstChild);
}
}
// Otherwise, just open up the fresh page.
else {
getTestFrame().src = suiteTable.rows[row].cells[0].getElementsByTagName("a")[0].href;
}
return false;
};
}
function isQueryParameterTrue(name) {
parameterValue = getQueryParameter(name);
return (parameterValue != null && parameterValue.toLowerCase() == "true");
}
function getQueryParameter(searchKey) {
var clauses = location.search.substr(1).split('&');
for (var i = 0; i < clauses.length; i++) {
var keyValuePair = clauses[i].split('=',2);
var key = unescape(keyValuePair[0]);
if (key == searchKey) {
return unescape(keyValuePair[1]);
}
}
return null;
}
function isNewWindow() {
return isQueryParameterTrue("newWindow");
}
function isAutomatedRun() {
return isQueryParameterTrue("auto");
}
function resetMetrics() {
numTestPasses = 0;
numTestFailures = 0;
numCommandPasses = 0;
numCommandFailures = 0;
numCommandErrors = 0;
startTime = new Date().getTime();
}
function runSingleTest() {
runAllTests = false;
resetMetrics();
startTest();
}
function startTest() {
removeLoadListener(getTestFrame(), startTest);
// Scroll to the top of the test frame
if (getTestFrame().contentWindow) {
getTestFrame().contentWindow.scrollTo(0,0);
}
else {
frames['testFrame'].scrollTo(0,0);
}
inputTable = getIframeDocument(getTestFrame()).getElementsByTagName("table")[0];
inputTableRows = inputTable.rows;
currentCommandRow = 0;
testFailed = false;
storedVars = new Object();
clearRowColours();
testLoop = initialiseTestLoop();
testLoop.start();
}
function clearRowColours() {
for (var i = 0; i <= inputTableRows.length - 1; i++) {
inputTableRows[i].bgColor = "white";
}
}
function startTestSuite() {
resetMetrics();
currentTestRow = 0;
runAllTests = true;
suiteFailed = false;
runNextTest();
}
function runNextTest() {
if (!runAllTests)
return;
suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0];
// Do not change the row color of the first row
if(currentTestRow > 0) {
// Make the previous row green or red depending if the test passed or failed
if(testFailed)
setCellColor(suiteTable.rows, currentTestRow, 0, failColor);
else
setCellColor(suiteTable.rows, currentTestRow, 0, passColor);
// Set the results from the previous test run
setResultsData(suiteTable, currentTestRow);
}
currentTestRow++;
// If we are done with all of the tests, set the title bar as pass or fail
if(currentTestRow >= suiteTable.rows.length) {
if(suiteFailed)
setCellColor(suiteTable.rows, 0, 0, failColor);
else
setCellColor(suiteTable.rows, 0, 0, passColor);
// If this is an automated run (i.e., build script), then submit
// the test results by posting to a form
if (isAutomatedRun())
postTestResults(suiteFailed, suiteTable);
}
else {
// Make the current row blue
setCellColor(suiteTable.rows, currentTestRow, 0, workingColor);
testLink = suiteTable.rows[currentTestRow].cells[0].getElementsByTagName("a")[0];
testLink.focus();
var testFrame = getTestFrame();
addLoadListener(testFrame, startTest);
selenium.browserbot.setIFrameLocation(testFrame, testLink.href);
}
}
function setCellColor(tableRows, row, col, colorStr) {
tableRows[row].cells[col].bgColor = colorStr;
}
// Sets the results from a test into a hidden column on the suite table. So,
// for each tests, the second column is set to the HTML from the test table.
function setResultsData(suiteTable, row) {
// Create a text node of the test table
var resultTable = getIframeDocument(getTestFrame()).body.innerHTML;
if (!resultTable) return;
var tableNode = suiteTable.ownerDocument.createElement("div");
tableNode.innerHTML = resultTable;
var new_column = suiteTable.ownerDocument.createElement("td");
new_column.appendChild(tableNode);
// Set the column to be invisible
new_column.style.cssText = "display: none;";
// Add the invisible column
suiteTable.rows[row].appendChild(new_column);
}
// Post the results to a servlet, CGI-script, etc. The URL of the
// results-handler defaults to "/postResults", but an alternative location
// can be specified by providing a "resultsUrl" query parameter.
//
// Parameters passed to the results-handler are:
// result: passed/failed depending on whether the suite passed or failed
// totalTime: the total running time in seconds for the suite.
//
// numTestPasses: the total number of tests which passed.
// numTestFailures: the total number of tests which failed.
//
// numCommandPasses: the total number of commands which passed.
// numCommandFailures: the total number of commands which failed.
// numCommandErrors: the total number of commands which errored.
//
// suite: the suite table, including the hidden column of test results
// testTable.1 to testTable.N: the individual test tables
//
function postTestResults(suiteFailed, suiteTable) {
form = document.createElement("form");
document.body.appendChild(form);
form.id = "resultsForm";
form.method="post";
form.target="myiframe";
var resultsUrl = getQueryParameter("resultsUrl");
if (!resultsUrl) {
resultsUrl = "./postResults";
}
var actionAndParameters = resultsUrl.split('?',2);
form.action = actionAndParameters[0];
var resultsUrlQueryString = actionAndParameters[1];
form.createHiddenField = function(name, value) {
input = document.createElement("input");
input.type = "hidden";
input.name = name;
input.value = value;
this.appendChild(input);
};
if (resultsUrlQueryString) {
var clauses = resultsUrlQueryString.split('&');
for (var i = 0; i < clauses.length; i++) {
var keyValuePair = clauses[i].split('=',2);
var key = unescape(keyValuePair[0]);
var value = unescape(keyValuePair[1]);
form.createHiddenField(key, value);
}
}
form.createHiddenField("result", suiteFailed == true ? "failed" : "passed");
form.createHiddenField("totalTime", Math.floor((currentTime - startTime) / 1000));
form.createHiddenField("numTestPasses", numTestPasses);
form.createHiddenField("numTestFailures", numTestFailures);
form.createHiddenField("numCommandPasses", numCommandPasses);
form.createHiddenField("numCommandFailures", numCommandFailures);
form.createHiddenField("numCommandErrors", numCommandErrors);
// Create an input for each test table. The inputs are named
// testTable.1, testTable.2, etc.
for (rowNum = 1; rowNum < suiteTable.rows.length;rowNum++) {
// If there is a second column, then add a new input
if (suiteTable.rows[rowNum].cells.length > 1) {
var resultCell = suiteTable.rows[rowNum].cells[1];
form.createHiddenField("testTable." + rowNum, resultCell.innerHTML);
// remove the resultCell, so it's not included in the suite HTML
resultCell.parentNode.removeChild(resultCell);
}
}
// Add HTML for the suite itself
form.createHiddenField("suite", suiteTable.parentNode.innerHTML);
form.submit();
document.body.removeChild(form);
}
function printMetrics() {
setText(document.getElementById("commandPasses"), numCommandPasses);
setText(document.getElementById("commandFailures"), numCommandFailures);
setText(document.getElementById("commandErrors"), numCommandErrors);
setText(document.getElementById("testRuns"), numTestPasses + numTestFailures);
setText(document.getElementById("testFailures"), numTestFailures);
currentTime = new Date().getTime();
timeDiff = currentTime - startTime;
totalSecs = Math.floor(timeDiff / 1000);
minutes = Math.floor(totalSecs / 60);
seconds = totalSecs % 60;
setText(document.getElementById("elapsedTime"), pad(minutes)+":"+pad(seconds));
}
// Puts a leading 0 on num if it is less than 10
function pad (num) {
return (num > 9) ? num : "0" + num;
}
/*
* Register all of the built-in command handlers with the CommandHandlerFactory.
* TODO work out an easy way for people to register handlers without modifying the Selenium sources.
*/
function registerCommandHandlers() {
commandFactory = new CommandHandlerFactory();
commandFactory.registerAll(selenium);
}
function initialiseTestLoop() {
testLoop = new TestLoop(commandFactory);
testLoop.getCommandInterval = function() { return runInterval; };
testLoop.firstCommand = nextCommand;
testLoop.nextCommand = nextCommand;
testLoop.commandStarted = commandStarted;
testLoop.commandComplete = commandComplete;
testLoop.commandError = commandError;
testLoop.testComplete = testComplete;
testLoop.pause = function() {
document.getElementById('continueTest').disabled = false;
};
return testLoop;
}
function nextCommand() {
if (currentCommandRow >= inputTableRows.length - 1) {
return null;
}
currentCommandRow++;
var commandName = getCellText(currentCommandRow, 0);
var target = getCellText(currentCommandRow, 1);
var value = getCellText(currentCommandRow, 2);
var command = new SeleniumCommand(commandName, target, removeNbsp(value));
return command;
}
function removeNbsp(value)
{
return value.replace(/\240/g, "");
}
function scrollIntoView(element) {
if (element.scrollIntoView) {
element.scrollIntoView(false);
return;
}
// TODO: work out how to scroll browsers that don't support
// scrollIntoView (like Konqueror)
}
function commandStarted() {
inputTableRows[currentCommandRow].bgColor = workingColor;
scrollIntoView(inputTableRows[currentCommandRow].cells[0]);
printMetrics();
}
function commandComplete(result) {
if (result.failed) {
setRowFailed(result.failureMessage, FAILURE);
} else if (result.passed) {
setRowPassed();
} else {
setRowWhite();
}
}
function commandError(errorMessage) {
setRowFailed(errorMessage, ERROR);
}
function setRowWhite() {
inputTableRows[currentCommandRow].bgColor = "white";
}
function setRowPassed() {
numCommandPasses += 1;
// Set cell background to green
inputTableRows[currentCommandRow].bgColor = passColor;
}
function setRowFailed(errorMsg, failureType) {
if (failureType == ERROR)
numCommandErrors += 1;
else if (failureType == FAILURE)
numCommandFailures += 1;
// Set cell background to red
inputTableRows[currentCommandRow].bgColor = failColor;
// Set error message
inputTableRows[currentCommandRow].cells[2].innerHTML = errorMsg;
inputTableRows[currentCommandRow].title = errorMsg;
testFailed = true;
suiteFailed = true;
}
function testComplete() {
if(testFailed) {
inputTableRows[0].bgColor = failColor;
numTestFailures += 1;
}
else {
inputTableRows[0].bgColor = passColor;
numTestPasses += 1;
}
printMetrics();
window.setTimeout("runNextTest()", 1);
}
function getCellText(rowNumber, columnNumber) {
var cell = inputTableRows[rowNumber].cells[columnNumber];
if (! cell.cachedText) {
cell.cachedText = getText(cell);
}
return cell.cachedText;
}
Selenium.prototype.doPause = function(waitTime) {
testLoop.pauseInterval = waitTime;
};