summaryrefslogtreecommitdiff
path: root/tests/FunctionalTests/selenium/selenium-testrunner.js
diff options
context:
space:
mode:
authorwei <>2005-12-10 11:49:29 +0000
committerwei <>2005-12-10 11:49:29 +0000
commit98215a603fb798cdb4178e49061977544aaa45b7 (patch)
tree282787037961f7466acbd174ce151cac6de71273 /tests/FunctionalTests/selenium/selenium-testrunner.js
parent25f9c45261aafd65477ad526e0b6ad3dca80803a (diff)
Initial import selenium functional test framework.
Diffstat (limited to 'tests/FunctionalTests/selenium/selenium-testrunner.js')
-rw-r--r--tests/FunctionalTests/selenium/selenium-testrunner.js597
1 files changed, 597 insertions, 0 deletions
diff --git a/tests/FunctionalTests/selenium/selenium-testrunner.js b/tests/FunctionalTests/selenium/selenium-testrunner.js
new file mode 100644
index 00000000..e786b29b
--- /dev/null
+++ b/tests/FunctionalTests/selenium/selenium-testrunner.js
@@ -0,0 +1,597 @@
+/*
+* 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.
+*
+*/
+
+// The current row in the list of tests (test suite)
+currentRowInSuite = 0;
+
+// Where are we at in the current test
+testTableRows = null;
+currentRowInTest = -1;
+currentCommandRow = null;
+
+// 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;
+
+// Colors used to provide feedback
+passColor = "#ccffcc";
+doneColor = "#eeffee";
+failColor = "#ffcccc";
+workingColor = "#ffffcc";
+
+// 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.resume();
+}
+
+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[currentRowInSuite+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);
+ }
+
+ testTable = getIframeDocument(getTestFrame()).getElementsByTagName("table")[0];
+ testTableRows = testTable.rows;
+ currentRowInTest = -1;
+ currentCommandRow = null;
+
+ testFailed = false;
+ storedVars = new Object();
+
+ resetTestTable();
+
+ testLoop = initialiseTestLoop();
+ testLoop.start();
+}
+
+function resetTestTable() {
+ for (var i = 0; i <= testTableRows.length - 1; i++) {
+ testTableRows[i].bgColor = "white";
+ }
+}
+
+function startTestSuite() {
+ resetMetrics();
+ currentRowInSuite = 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 (currentRowInSuite > 0) {
+ // Provide test-status feedback
+ if (testFailed) {
+ setCellColor(suiteTable.rows, currentRowInSuite, 0, failColor);
+ } else {
+ setCellColor(suiteTable.rows, currentRowInSuite, 0, passColor);
+ }
+
+ // Set the results from the previous test run
+ setResultsData(suiteTable, currentRowInSuite);
+ }
+
+ currentRowInSuite++;
+
+ // If we are done with all of the tests, set the title bar as pass or fail
+ if (currentRowInSuite >= 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, currentRowInSuite, 0, workingColor);
+
+ testLink = suiteTable.rows[currentRowInSuite].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.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 findNextCommandRow() {
+ // Return the next row with >= 3 cells
+ while (currentRowInTest < testTableRows.length - 1) {
+ currentRowInTest++;
+ currentCommandRow = testTableRows[currentRowInTest];
+ if (currentCommandRow.cells.length >= 3) {
+ return;
+ }
+ }
+ currentCommandRow = null;
+}
+
+function nextCommand() {
+ findNextCommandRow();
+ if (currentCommandRow == null) {
+ return null;
+ }
+ var row = currentCommandRow;
+ if (! row.cachedValue) {
+ row.cachedValue = removeNbsp(getText(row.cells[2]));
+ }
+ return new SeleniumCommand(getText(row.cells[0]),
+ getText(row.cells[1]),
+ row.cachedValue);
+}
+
+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() {
+ currentCommandRow.bgColor = workingColor;
+ scrollIntoView(currentCommandRow.cells[0]);
+ printMetrics();
+}
+
+function commandComplete(result) {
+ if (result.failed) {
+ numCommandFailures += 1;
+ recordFailure(result.failureMessage);
+ } else if (result.passed) {
+ numCommandPasses += 1;
+ currentCommandRow.bgColor = passColor;
+ } else {
+ currentCommandRow.bgColor = doneColor;
+ }
+}
+
+function commandError(errorMessage) {
+ numCommandErrors += 1;
+ recordFailure(errorMessage);
+}
+
+function recordFailure(errorMsg) {
+ // Set cell background to red
+ currentCommandRow.bgColor = failColor;
+
+ // Set error message
+ currentCommandRow.cells[2].innerHTML = errorMsg;
+ currentCommandRow.title = errorMsg;
+ testFailed = true;
+ suiteFailed = true;
+}
+
+function testComplete() {
+ if(testFailed) {
+ testTableRows[0].bgColor = failColor;
+ numTestFailures += 1;
+ }
+ else {
+ testTableRows[0].bgColor = passColor;
+ numTestPasses += 1;
+ }
+
+ printMetrics();
+
+ window.setTimeout("runNextTest()", 1);
+}
+
+Selenium.prototype.doPause = function(waitTime) {
+ testLoop.pauseInterval = waitTime;
+};
+
+Selenium.prototype.doBreak = function() {
+ document.getElementById('modeStep').checked = true;
+ runInterval = -1;
+};