From 7eafd95bfa21c9c8603e9c437a9d96dc880d92e7 Mon Sep 17 00:00:00 2001 From: wei <> Date: Sat, 30 Sep 2006 05:18:39 +0000 Subject: Update selenium to 0.8 --- .../selenium/core/scripts/selenium-testrunner.js | 1549 +++++++++++++------- 1 file changed, 998 insertions(+), 551 deletions(-) (limited to 'tests/test_tools/selenium/core/scripts/selenium-testrunner.js') diff --git a/tests/test_tools/selenium/core/scripts/selenium-testrunner.js b/tests/test_tools/selenium/core/scripts/selenium-testrunner.js index 1ced0a11..b5104d39 100644 --- a/tests/test_tools/selenium/core/scripts/selenium-testrunner.js +++ b/tests/test_tools/selenium/core/scripts/selenium-testrunner.js @@ -15,433 +15,584 @@ * */ -// The current row in the list of tests (test suite) -currentRowInSuite = 0; +// An object representing the current test, used external +var currentTest = null; // TODO: get rid of this global, which mirrors the htmlTestRunner.currentTest +var selenium = null; + +var htmlTestRunner; +var HtmlTestRunner = Class.create(); +Object.extend(HtmlTestRunner.prototype, { + initialize: function() { + this.metrics = new Metrics(); + this.controlPanel = new HtmlTestRunnerControlPanel(); + this.htmlTestSuite = null; + this.testFailed = false; + this.currentTest = null; + this.runAllTests = false; + this.appWindow = null; + // we use a timeout here to make sure the LOG has loaded first, so we can see _every_ error + setTimeout(function() { + this.loadSuiteFrame(); + }.bind(this), 500); + }, -// An object representing the current test -currentTest = null; + markFailed: function() { + this.testFailed = true; + this.htmlTestSuite.markFailed(); + }, -// Whether or not the jsFT should run all tests in the suite -runAllTests = false; + loadSuiteFrame: function() { + if (selenium == null) { + selenium = Selenium.createForWindow(this._getApplicationWindow()); + this._registerCommandHandlers(); + } + this.controlPanel.setHighlightOption(); + var testSuiteName = this.controlPanel.getTestSuiteName(); + if (testSuiteName) { + suiteFrame.load(testSuiteName, this._onloadTestSuite.bind(this)); + } + }, -// Whether or not the current test has any errors; -testFailed = false; -suiteFailed = false; + _getApplicationWindow: function () { + if (this.controlPanel.isMultiWindowMode()) { + return this._getSeparateApplicationWindow(); + } + return $('myiframe').contentWindow; + }, -// Colors used to provide feedback -passColor = "#ccffcc"; -doneColor = "#eeffee"; -failColor = "#ffcccc"; -workingColor = "#ffffcc"; + _getSeparateApplicationWindow: function () { + if (this.appWindow == null) { + this.appWindow = openSeparateApplicationWindow('TestRunner-splash.html'); + } + return this.appWindow; + }, -// Holds the handlers for each command. -commandHandlers = null; + _onloadTestSuite:function () { + this.htmlTestSuite = new HtmlTestSuite(suiteFrame.getDocument()); + if (! this.htmlTestSuite.isAvailable()) { + return; + } + if (this.controlPanel.isAutomatedRun()) { + htmlTestRunner.startTestSuite(); + } else if (this.controlPanel.getAutoUrl()) { + //todo what is the autourl doing, left to check it out + addLoadListener(this._getApplicationWindow(), this._startSingleTest.bind(this)); + this._getApplicationWindow().src = this.controlPanel.getAutoUrl(); + } else { + this.htmlTestSuite.getSuiteRows()[0].loadTestCase(); + } + }, -// The number of tests run -numTestPasses = 0; + _startSingleTest:function () { + removeLoadListener(getApplicationWindow(), this._startSingleTest.bind(this)); + var singleTestName = this.controlPanel.getSingleTestName(); + testFrame.load(singleTestName, this.startTest.bind(this)); + }, -// The number of tests that have failed -numTestFailures = 0; + _registerCommandHandlers: function () { + this.commandFactory = new CommandHandlerFactory(); + this.commandFactory.registerAll(selenium); + }, -// The number of commands which have passed -numCommandPasses = 0; + startTestSuite: function() { + this.controlPanel.reset(); + this.metrics.resetMetrics(); + this.htmlTestSuite.reset(); + this.runAllTests = true; + this.runNextTest(); + }, -// The number of commands which have failed -numCommandFailures = 0; + runNextTest: function () { + if (!this.runAllTests) { + return; + } + this.htmlTestSuite.runNextTestInSuite(); + }, -// The number of commands which have caused errors (element not found) -numCommandErrors = 0; + startTest: function () { + this.controlPanel.reset(); + testFrame.scrollToTop(); + //todo: move testFailed and storedVars to TestCase + this.testFailed = false; + storedVars = new Object(); + this.currentTest = new HtmlRunnerTestLoop(testFrame.getCurrentTestCase(), this.metrics, this.commandFactory); + currentTest = this.currentTest; + this.currentTest.start(); + }, -// The time that the test was started. -startTime = null; + runSingleTest:function() { + this.runAllTests = false; + this.metrics.resetMetrics(); + this.startTest(); + } +}); -// The current time. -currentTime = null; +var FeedbackColors = Class.create(); +Object.extend(FeedbackColors, { + passColor : "#ccffcc", + doneColor : "#eeffee", + failColor : "#ffcccc", + workingColor : "#ffffcc", + breakpointColor : "#cccccc" +}); -// An simple enum for failureType -ERROR = 0; -FAILURE = 1; -runInterval = 0; +var runInterval = 0; -queryString = null; -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; - } - } -} +/** SeleniumFrame encapsulates an iframe element */ +var SeleniumFrame = Class.create(); +Object.extend(SeleniumFrame.prototype, { -function continueCurrentTest() { - document.getElementById('continueTest').disabled = true; - testLoop.resume(); -} + initialize : function(frame) { + this.frame = frame; + addLoadListener(this.frame, this._handleLoad.bind(this)); + }, -function getApplicationFrame() { - return document.getElementById('myiframe'); -} + getDocument : function() { + return this.frame.contentWindow.document; + }, -function getSuiteFrame() { - return document.getElementById('testSuiteFrame'); -} + _handleLoad: function() { + this._onLoad(); + if (this.loadCallback) { + this.loadCallback(); + this.loadCallback = null; + } + }, -function getTestFrame(){ - return document.getElementById('testFrame'); -} + _onLoad: function() { + }, -function loadAndRunIfAuto() { - loadSuiteFrame(); -} + scrollToTop : function() { + this.frame.contentWindow.scrollTo(0, 0); + }, -function start() { - queryString = null; - setRunInterval(); - loadSuiteFrame(); -} + _setLocation: function(location) { + if (browserVersion.isSafari) { + // safari doesn't reload the page when the location equals to current location. + // hence, set the location to blank so that the page will reload automatically. + this.frame.src = "about:blank"; + this.frame.src = location; + } else { + this.frame.contentWindow.location.replace(location); + } + }, -function loadSuiteFrame() { - var testAppFrame = document.getElementById('myiframe'); - selenium = Selenium.createForFrame(testAppFrame); - registerCommandHandlers(); + load: function(/* url, [callback] */) { + if (arguments.length > 1) { + this.loadCallback = arguments[1]; - //set the runInterval if there is a queryParameter for it - var tempRunInterval = getQueryParameter("runInterval"); - if (tempRunInterval) { - runInterval = tempRunInterval; + } + this._setLocation(arguments[0]); } - document.getElementById("modeRun").onclick = setRunInterval; - document.getElementById('modeWalk').onclick = setRunInterval; - document.getElementById('modeStep').onclick = setRunInterval; - document.getElementById('continueTest').onclick = continueCurrentTest; +}); + +/** HtmlTestFrame - encapsulates the test-case iframe element */ +var HtmlTestFrame = Class.create(); +Object.extend(HtmlTestFrame.prototype, SeleniumFrame.prototype); +Object.extend(HtmlTestFrame.prototype, { - var testSuiteName = getQueryParameter("test"); + _onLoad: function() { + this.setCurrentTestCase(); + }, + + setCurrentTestCase: function() { + //todo: this is not good looking + this.currentTestCase = new HtmlTestCase(this.getDocument(), htmlTestRunner.htmlTestSuite.getCurrentRow()); + }, - if (testSuiteName) { - addLoadListener(getSuiteFrame(), onloadTestSuite); - getSuiteFrame().src = testSuiteName; - } else { - onloadTestSuite(); + getCurrentTestCase: function() { + return this.currentTestCase; } -} -function startSingleTest() { - removeLoadListener(getApplicationFrame(), startSingleTest); - var singleTestName = getQueryParameter("singletest"); - addLoadListener(getTestFrame(), startTest); - getTestFrame().src = singleTestName; +}); + +function onSeleniumLoad() { + suiteFrame = new SeleniumFrame(getSuiteFrame()); + testFrame = new HtmlTestFrame(getTestFrame()); + htmlTestRunner = new HtmlTestRunner(); } -function getIframeDocument(iframe) -{ - if (iframe.contentDocument) { - return iframe.contentDocument; - } - else { - return iframe.contentWindow.document; + +var suiteFrame; +var testFrame; +function getSuiteFrame() { + var f = $('testSuiteFrame'); + if (f == null) { + f = top; + // proxyInjection mode does not set myiframe } + return f; } -function onloadTestSuite() { - removeLoadListener(getSuiteFrame(), onloadTestSuite); - - // Add an onclick function to each link in all suite tables - var allTables = getIframeDocument(getSuiteFrame()).getElementsByTagName("table"); - for (var tableNum = 0; tableNum < allTables.length; tableNum++) - { - var skippedTable = allTables[tableNum]; - for(rowNum = 1;rowNum < skippedTable.rows.length; rowNum++) { - addOnclick(skippedTable, rowNum); - } +function getTestFrame() { + var f = $('testFrame'); + if (f == null) { + f = top; + // proxyInjection mode does not set myiframe } + return f; +} + +var HtmlTestRunnerControlPanel = Class.create(); +Object.extend(HtmlTestRunnerControlPanel.prototype, URLConfiguration.prototype); +Object.extend(HtmlTestRunnerControlPanel.prototype, { + initialize: function() { + this._acquireQueryString(); + + this.runInterval = 0; - suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0]; - if (suiteTable!=null) { + this.highlightOption = $('highlightOption'); + this.pauseButton = $('pauseTest'); + this.stepButton = $('stepTest'); - if (isAutomatedRun()) { - startTestSuite(); - } else if (getQueryParameter("autoURL")) { + this.highlightOption.onclick = (function() { + this.setHighlightOption(); + }).bindAsEventListener(this); + this.pauseButton.onclick = this.pauseCurrentTest.bindAsEventListener(this); + this.stepButton.onclick = this.stepCurrentTest.bindAsEventListener(this); - addLoadListener(getApplicationFrame(), startSingleTest); + this.speedController = new Control.Slider('speedHandle', 'speedTrack', { + range: $R(0, 1000), + onSlide: this.setRunInterval.bindAsEventListener(this), + onChange: this.setRunInterval.bindAsEventListener(this) + }); - getApplicationFrame().src = getQueryParameter("autoURL"); + this._parseQueryParameter(); + }, + + setHighlightOption: function () { + var isHighlight = this.highlightOption.checked; + selenium.browserbot.getCurrentPage().setHighlightElement(isHighlight); + }, - } else { - testLink = suiteTable.rows[currentRowInSuite+1].cells[0].getElementsByTagName("a")[0]; - getTestFrame().src = testLink.href; + _parseQueryParameter: function() { + var tempRunInterval = this._getQueryParameter("runInterval"); + if (tempRunInterval) { + this.setRunInterval(tempRunInterval); } - } -} + this.highlightOption.checked = this._getQueryParameter("highlight"); + }, -// 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; + setRunInterval: function(runInterval) { + this.runInterval = runInterval; + }, - // For mozilla-like browsers - if(eventObj) - srcObj = eventObj.target; + setToPauseAtNextCommand: function() { + this.runInterval = -1; + }, - // For IE-like browsers - else if (getSuiteFrame().contentWindow.event) - srcObj = getSuiteFrame().contentWindow.event.srcElement; + pauseCurrentTest: function () { + this.setToPauseAtNextCommand(); + this._switchPauseButtonToContinue(); + }, - // The target row (the event source is not consistently reported by browsers) - row = srcObj.parentNode.parentNode.rowIndex || srcObj.parentNode.parentNode.parentNode.rowIndex; + continueCurrentTest: function () { + this.reset(); + currentTest.resume(); + }, - // 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; - } + reset: function() { + this.runInterval = this.speedController.value; + this._switchContinueButtonToPause(); + }, - return false; - }; -} + _switchContinueButtonToPause: function() { + this.pauseButton.innerHTML = "Pause"; + this.pauseButton.onclick = this.pauseCurrentTest.bindAsEventListener(this); + }, -function isQueryParameterTrue(name) { - parameterValue = getQueryParameter(name); - return (parameterValue != null && parameterValue.toLowerCase() == "true"); -} + _switchPauseButtonToContinue: function() { + $('stepTest').disabled = false; + this.pauseButton.innerHTML = "Continue"; + this.pauseButton.onclick = this.continueCurrentTest.bindAsEventListener(this); + }, -function getQueryString() { - if (queryString != null) return queryString; - if (browserVersion.isHTA) { - var args = extractArgs(); - if (args.length < 2) return null; - queryString = args[1]; - return queryString; - } else { - return location.search.substr(1); - } -} + stepCurrentTest: function () { + this.setToPauseAtNextCommand(); + currentTest.resume(); + }, -function extractArgs() { - var str = SeleniumHTARunner.commandLine; - if (str == null || str == "") return new Array(); - var matches = str.match(/(?:"([^"]+)"|(?!"([^"]+)")(\S+))/g); - // We either want non quote stuff ([^"]+) surrounded by quotes - // or we want to look-ahead, see that the next character isn't - // a quoted argument, and then grab all the non-space stuff - // this will return for the line: "foo" bar - // the results "\"foo\"" and "bar" - - // So, let's unquote the quoted arguments: - var args = new Array; - for (var i = 0; i < matches.length; i++) { - args[i] = matches[i]; - args[i] = args[i].replace(/^"(.*)"$/, "$1"); - } - return args; -} + isAutomatedRun: function() { + return this._isQueryParameterTrue("auto"); + }, + + shouldSaveResultsToFile: function() { + return this._isQueryParameterTrue("save"); + }, + + closeAfterTests: function() { + return this._isQueryParameterTrue("close"); + }, -function getQueryParameter(searchKey) { - var str = getQueryString(); - if (str == null) return null; - var clauses = str.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]); + getTestSuiteName: function() { + return this._getQueryParameter("test"); + }, + + getSingleTestName: function() { + return this._getQueryParameter("singletest"); + }, + + getAutoUrl: function() { + return this._getQueryParameter("autoURL"); + }, + + getResultsUrl: function() { + return this._getQueryParameter("resultsUrl"); + }, + + _acquireQueryString: function() { + if (this.queryString) return; + if (browserVersion.isHTA) { + var args = this._extractArgs(); + if (args.length < 2) return null; + this.queryString = args[1]; + } else { + this.queryString = location.search.substr(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(); -} +var AbstractResultAwareRow = Class.create(); +Object.extend(AbstractResultAwareRow.prototype, { -function runSingleTest() { - runAllTests = false; - resetMetrics(); - startTest(); -} + initialize: function(trElement) { + this.trElement = trElement; + }, -function startTest() { - removeLoadListener(getTestFrame(), startTest); + markWorking: function() { + this.trElement.bgColor = FeedbackColors.workingColor; + safeScrollIntoView(this.trElement); + }, - // Scroll to the top of the test frame - if (getTestFrame().contentWindow) { - getTestFrame().contentWindow.scrollTo(0,0); - } - else { - frames['testFrame'].scrollTo(0,0); + markPassed: function() { + this.trElement.bgColor = FeedbackColors.passColor; + }, + + markDone: function() { + this.trElement.bgColor = FeedbackColors.doneColor; + }, + + markFailed: function() { + this.trElement.bgColor = FeedbackColors.failColor; } - currentTest = new HtmlTest(getIframeDocument(getTestFrame())); +}); - testFailed = false; - storedVars = new Object(); +var HtmlTestCaseRow = Class.create(); +Object.extend(HtmlTestCaseRow.prototype, AbstractResultAwareRow.prototype); +Object.extend(HtmlTestCaseRow.prototype, { - testLoop = initialiseTestLoop(); - testLoop.start(); -} + getCommand: function () { + return new SeleniumCommand(getText(this.trElement.cells[0]), + getText(this.trElement.cells[1]), + getText(this.trElement.cells[2]), + this.isBreakpoint()); + }, -function HtmlTest(testDocument) { - this.init(testDocument); -} + markFailed: function(errorMsg) { + this.trElement.bgColor = FeedbackColors.failColor; + this.setMessage(errorMsg); + }, -HtmlTest.prototype = { + setMessage: function(message) { + this.trElement.cells[2].innerHTML = message; + }, - init: function(testDocument) { - this.document = testDocument; - this.document.bgColor = ""; - this.currentRow = null; - this.commandRows = new Array(); - this.headerRow = null; - var tables = this.document.getElementsByTagName("table"); - for (var i = 0; i < tables.length; i++) { - var candidateRows = tables[i].rows; - for (var j = 0; j < candidateRows.length; j++) { - if (!this.headerRow) { - this.headerRow = candidateRows[j]; - } - if (candidateRows[j].cells.length >= 3) { - this.addCommandRow(candidateRows[j]); - } + reset: function() { + this.trElement.bgColor = ''; + var thirdCell = this.trElement.cells[2]; + if (thirdCell) { + if (thirdCell.originalHTML) { + thirdCell.innerHTML = thirdCell.originalHTML; + } else { + thirdCell.originalHTML = thirdCell.innerHTML; } } }, - addCommandRow: function(row) { - if (row.cells[2] && row.cells[2].originalHTML) { - row.cells[2].innerHTML = row.cells[2].originalHTML; + onClick: function() { + if (this.trElement.isBreakpoint == undefined) { + this.trElement.isBreakpoint = true; + this.trElement.beforeBackgroundColor = Element.getStyle(this.trElement, "backgroundColor"); + Element.setStyle(this.trElement, {"background-color" : FeedbackColors.breakpointColor}); + } else { + this.trElement.isBreakpoint = undefined; + Element.setStyle(this.trElement, {"background-color" : this.trElement.beforeBackgroundColor}); } - row.bgColor = ""; - this.commandRows.push(row); }, - nextCommand: function() { - if (this.commandRows.length > 0) { - this.currentRow = this.commandRows.shift(); - } else { - this.currentRow = null; + addBreakpointSupport: function() { + Element.setStyle(this.trElement, {"cursor" : "pointer"}); + this.trElement.onclick = function() { + this.onClick(); + }.bindAsEventListener(this); + }, + + isBreakpoint: function() { + if (this.trElement.isBreakpoint == undefined || this.trElement.isBreakpoint == null) { + return false } - return this.currentRow; + return this.trElement.isBreakpoint; } +}); + +var HtmlTestSuiteRow = Class.create(); +Object.extend(HtmlTestSuiteRow.prototype, AbstractResultAwareRow.prototype); +Object.extend(HtmlTestSuiteRow.prototype, { + + initialize: function(trElement, testFrame, htmlTestSuite) { + this.trElement = trElement; + this.testFrame = testFrame; + this.htmlTestSuite = htmlTestSuite; + this.link = trElement.getElementsByTagName("a")[0]; + this.link.onclick = this._onClick.bindAsEventListener(this); + }, -}; + reset: function() { + this.trElement.bgColor = ''; + }, -function startTestSuite() { - resetMetrics(); - currentRowInSuite = 0; - runAllTests = true; - suiteFailed = false; + _onClick: function() { + // todo: just send a message to the testSuite + this.loadTestCase(null); + return false; + }, - runNextTest(); -} + loadTestCase: function(onloadFunction) { + this.htmlTestSuite.currentRowInSuite = this.trElement.rowIndex - 1; + // If the row has a stored results table, use that + var resultsFromPreviousRun = this.trElement.cells[1]; + if (resultsFromPreviousRun) { + // this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML); + var testBody = this.testFrame.getDocument().body; + testBody.innerHTML = resultsFromPreviousRun.innerHTML; + testFrame.setCurrentTestCase(); + if (onloadFunction) { + onloadFunction(); + } + } else { + this.testFrame.load(this.link.href, onloadFunction); + } + }, -function runNextTest() { - if (!runAllTests) - return; + saveTestResults: function() { + // todo: GLOBAL ACCESS! + var resultHTML = this.testFrame.getDocument().body.innerHTML; + if (!resultHTML) return; - suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0]; + // todo: why create this div? + var divElement = this.trElement.ownerDocument.createElement("div"); + divElement.innerHTML = resultHTML; - // 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); - } + var hiddenCell = this.trElement.ownerDocument.createElement("td"); + hiddenCell.appendChild(divElement); + hiddenCell.style.display = "none"; - // Set the results from the previous test run - setResultsData(suiteTable, currentRowInSuite); + this.trElement.appendChild(hiddenCell); } - 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); +var HtmlTestSuite = Class.create(); +Object.extend(HtmlTestSuite.prototype, { + + initialize: function(suiteDocument) { + this.suiteDocument = suiteDocument; + this.suiteRows = this._collectSuiteRows(); + this.titleRow = this.getTestTable().rows[0]; + this.title = this.titleRow.cells[0].innerHTML; + this.reset(); + }, + + reset: function() { + this.failed = false; + this.currentRowInSuite = -1; + this.titleRow.bgColor = ''; + this.suiteRows.each(function(row) { + row.reset(); + }); + }, + + getTitle: function() { + return this.title; + }, + + getSuiteRows: function() { + return this.suiteRows; + }, + + getTestTable: function() { + var tables = $A(this.suiteDocument.getElementsByTagName("table")); + return tables[0]; + }, + + isAvailable: function() { + return this.getTestTable() != null; + }, + + _collectSuiteRows: function () { + var result = []; + for (rowNum = 1; rowNum < this.getTestTable().rows.length; rowNum++) { + var rowElement = this.getTestTable().rows[rowNum]; + result.push(new HtmlTestSuiteRow(rowElement, testFrame, this)); } + return result; + }, - // 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); - } + getCurrentRow: function() { + return this.suiteRows[this.currentRowInSuite]; + }, - else { - // Make the current row blue - setCellColor(suiteTable.rows, currentRowInSuite, 0, workingColor); + markFailed: function() { + this.failed = true; + this.titleRow.bgColor = FeedbackColors.failColor; + }, - testLink = suiteTable.rows[currentRowInSuite].cells[0].getElementsByTagName("a")[0]; - testLink.focus(); + markDone: function() { + if (!this.failed) { + this.titleRow.bgColor = FeedbackColors.passColor; + } + }, - var testFrame = getTestFrame(); - addLoadListener(testFrame, startTest); + _startCurrentTestCase: function() { + this.getCurrentRow().markWorking(); + this.getCurrentRow().loadTestCase(htmlTestRunner.startTest.bind(htmlTestRunner)); + }, - selenium.browserbot.setIFrameLocation(testFrame, testLink.href); - } -} + _onTestSuiteComplete: function() { + this.markDone(); + new TestResult(this.failed, this.getTestTable()).post(); + }, -function setCellColor(tableRows, row, col, colorStr) { - tableRows[row].cells[col].bgColor = colorStr; -} + _updateSuiteWithResultOfPreviousTest: function() { + if (this.currentRowInSuite >= 0) { + this.getCurrentRow().saveTestResults(); + } + }, -// 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; + runNextTestInSuite: function() { + this._updateSuiteWithResultOfPreviousTest(); + this.currentRowInSuite++; - var tableNode = suiteTable.ownerDocument.createElement("div"); - tableNode.innerHTML = resultTable; + // If we are done with all of the tests, set the title bar as pass or fail + if (this.currentRowInSuite >= this.suiteRows.length) { + this._onTestSuiteComplete(); + } else { + this._startCurrentTestCase(); + } + } - var new_column = suiteTable.ownerDocument.createElement("td"); - new_column.appendChild(tableNode); - // Set the column to be invisible - new_column.style.display = "none"; - // Add the invisible column - suiteTable.rows[row].appendChild(new_column); -} +}); + +var TestResult = Class.create(); +Object.extend(TestResult.prototype, { // Post the results to a servlet, CGI-script, etc. The URL of the // results-handler defaults to "/postResults", but an alternative location @@ -461,288 +612,584 @@ function setResultsData(suiteTable, row) { // suite: the suite table, including the hidden column of test results // testTable.1 to testTable.N: the individual test tables // -function postTestResults(suiteFailed, suiteTable) { + initialize: function (suiteFailed, suiteTable) { + this.controlPanel = htmlTestRunner.controlPanel; + this.metrics = htmlTestRunner.metrics; + this.suiteFailed = suiteFailed; + this.suiteTable = suiteTable; + }, - form = document.createElement("form"); - document.body.appendChild(form); + post: function () { + if (!this.controlPanel.isAutomatedRun()) { + return; + } + var form = document.createElement("form"); + document.body.appendChild(form); - form.id = "resultsForm"; - form.method="post"; - form.target="myiframe"; + form.id = "resultsForm"; + form.method = "post"; + form.target = "myiframe"; - var resultsUrl = getQueryParameter("resultsUrl"); - if (!resultsUrl) { - resultsUrl = "./postResults"; - } + var resultsUrl = this.controlPanel.getResultsUrl(); + if (!resultsUrl) { + resultsUrl = "./postResults"; + } - var actionAndParameters = resultsUrl.split('?',2); - form.action = actionAndParameters[0]; - var resultsUrlQueryString = actionAndParameters[1]; + 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 = function(name, value) { - input = document.createElement("input"); - input.type = "hidden"; - input.name = name; - input.value = value; - this.appendChild(input); - }; + form.createHiddenField("selenium.version", Selenium.version); + form.createHiddenField("selenium.revision", Selenium.revision); + + form.createHiddenField("result", this.suiteFailed ? "failed" : "passed"); + + form.createHiddenField("totalTime", Math.floor((this.metrics.currentTime - this.metrics.startTime) / 1000)); + form.createHiddenField("numTestPasses", this.metrics.numTestPasses); + form.createHiddenField("numTestFailures", this.metrics.numTestFailures); + form.createHiddenField("numCommandPasses", this.metrics.numCommandPasses); + form.createHiddenField("numCommandFailures", this.metrics.numCommandFailures); + form.createHiddenField("numCommandErrors", this.metrics.numCommandErrors); + + // Create an input for each test table. The inputs are named + // testTable.1, testTable.2, etc. + for (rowNum = 1; rowNum < this.suiteTable.rows.length; rowNum++) { + // If there is a second column, then add a new input + if (this.suiteTable.rows[rowNum].cells.length > 1) { + var resultCell = this.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); + } + } + + form.createHiddenField("numTestTotal", rowNum); - 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); + // Add HTML for the suite itself + form.createHiddenField("suite", this.suiteTable.parentNode.innerHTML); + + if (this.controlPanel.shouldSaveResultsToFile()) { + this._saveToFile(resultsUrl, form); + } else { + form.submit(); } - } + document.body.removeChild(form); + if (this.controlPanel.closeAfterTests()) { + window.top.close(); + } + }, - form.createHiddenField("selenium.version", Selenium.version); - form.createHiddenField("selenium.revision", Selenium.revision); - - 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); + _saveToFile: function (fileName, form) { + // This only works when run as an IE HTA + var inputs = new Object(); + for (var i = 0; i < form.elements.length; i++) { + inputs[form.elements[i].name] = form.elements[i].value; } + var objFSO = new ActiveXObject("Scripting.FileSystemObject") + var scriptFile = objFSO.CreateTextFile(fileName); + scriptFile.WriteLine("\n

Test suite results

" + + "\n\n\n\n\n\n" + + "\n\n\n\n\n" + + "\n\n\n\n" + + "\n\n\n\n" + + "\n\n\n\n" + + "\n\n\n\n" + + "\n\n\n\n" + + "\n\n\n"); + var testNum = inputs["numTestTotal"]; + for (var rowNum = 1; rowNum < testNum; rowNum++) { + scriptFile.WriteLine("\n\n\n"); + } + scriptFile.WriteLine("
result:" + inputs["result"] + "
totalTime:" + inputs["totalTime"] + "
numTestPasses:" + inputs["numTestPasses"] + "
numTestFailures:" + inputs["numTestFailures"] + "
numCommandPasses:" + inputs["numCommandPasses"] + "
numCommandFailures:" + inputs["numCommandFailures"] + "
numCommandErrors:" + inputs["numCommandErrors"] + "
" + inputs["suite"] + " 
" + inputs["testTable." + rowNum] + " 
"); + scriptFile.Close(); } - - form.createHiddenField("numTestTotal", rowNum); +}); - // Add HTML for the suite itself - form.createHiddenField("suite", suiteTable.parentNode.innerHTML); +/** HtmlTestCase encapsulates an HTML test document */ +var HtmlTestCase = Class.create(); +Object.extend(HtmlTestCase.prototype, { - if (isQueryParameterTrue("save")) { - saveToFile(resultsUrl, form); - } else { - form.submit(); - } - document.body.removeChild(form); - if (isQueryParameterTrue("close")) { - window.top.close(); - } -} + initialize: function(testDocument, htmlTestSuiteRow) { + if (testDocument == null) { + throw "testDocument should not be null"; + } + if (htmlTestSuiteRow == null) { + throw "htmlTestSuiteRow should not be null"; + } + this.testDocument = testDocument; + this.htmlTestSuiteRow = htmlTestSuiteRow; + this.commandRows = this._collectCommandRows(); + this.nextCommandRowIndex = 0; + this._addBreakpointSupport(); + }, -function saveToFile(fileName, form) { - // This only works when run as an IE HTA - var inputs = new Object(); - for (var i = 0; i < form.elements.length; i++) { - inputs[form.elements[i].name] = form.elements[i].value; - } - var objFSO = new ActiveXObject("Scripting.FileSystemObject") - var scriptFile = objFSO.CreateTextFile(fileName); - scriptFile.WriteLine("\n

Test suite results

" + - "\n\n\n\n\n\n" + - "\n\n\n\n\n" + - "\n\n\n\n" + - "\n\n\n\n" + - "\n\n\n\n" + - "\n\n\n\n" + - "\n\n\n\n" + - "\n\n\n"); - var testNum = inputs["numTestTotal"]; - for (var rowNum = 1; rowNum < testNum; rowNum++) { - scriptFile.WriteLine("\n\n\n"); - } - scriptFile.WriteLine("
result:" + inputs["result"] + "
totalTime:" + inputs["totalTime"] + "
numTestPasses:" + inputs["numTestPasses"] + "
numTestFailures:" + inputs["numTestFailures"] + "
numCommandPasses:" + inputs["numCommandPasses"] + "
numCommandFailures:" + inputs["numCommandFailures"] + "
numCommandErrors:" + inputs["numCommandErrors"] + "
" + inputs["suite"] + " 
" + inputs["testTable." + rowNum] + " 
"); - scriptFile.Close(); -} + _collectCommandRows: function () { + var commandRows = []; + var tables = $A(this.testDocument.getElementsByTagName("table")); + var self = this; + tables.each(function (table) { + $A(table.rows).each(function(candidateRow) { + if (self.isCommandRow(candidateRow)) { + commandRows.push(new HtmlTestCaseRow(candidateRow)); + } + }.bind(this)); + }); + return commandRows; + }, -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); + isCommandRow: function (row) { + return row.cells.length >= 3; + }, - currentTime = new Date().getTime(); + reset: function() { + /** + * reset the test to runnable state + */ + this.nextCommandRowIndex = 0; + + this._setTitleColor(''); + this.commandRows.each(function(row) { + row.reset(); + }); + + // remove any additional fake "error" row added to the end of the document + var errorElement = this.testDocument.getElementById('error'); + if (errorElement) { + Element.remove(errorElement); + } + }, - timeDiff = currentTime - startTime; - totalSecs = Math.floor(timeDiff / 1000); + getCommandRows: function () { + return this.commandRows; + }, - minutes = Math.floor(totalSecs / 60); - seconds = totalSecs % 60; + _setTitleColor: function(color) { + var headerRow = this.testDocument.getElementsByTagName("tr")[0]; + if (headerRow) { + headerRow.bgColor = color; + } + }, - setText(document.getElementById("elapsedTime"), pad(minutes)+":"+pad(seconds)); -} + markFailed: function() { + this._setTitleColor(FeedbackColors.failColor); + this.htmlTestSuiteRow.markFailed(); + }, -// Puts a leading 0 on num if it is less than 10 -function pad (num) { - return (num > 9) ? num : "0" + num; -} + markPassed: function() { + this._setTitleColor(FeedbackColors.passColor); + this.htmlTestSuiteRow.markPassed(); + }, -/* - * 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); + addErrorMessage: function(errorMsg, currentRow) { + if (currentRow) { + currentRow.markFailed(errorMsg); + } else { + var errorElement = this.testDocument.createElement("p"); + errorElement.id = "error"; + errorElement.innerHTML = errorMsg; + this.testDocument.body.appendChild(errorElement); + Element.setStyle(errorElement, {'backgroundColor': FeedbackColors.failColor}); + } + }, -} + _addBreakpointSupport: function() { + this.commandRows.each(function(row) { + row.addBreakpointSupport(); + }); + }, -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; -} + hasMoreCommandRows: function() { + return this.nextCommandRowIndex < this.commandRows.length; + }, -function nextCommand() { - var row = currentTest.nextCommand(); - if (row == null) { + getNextCommandRow: function() { + if (this.hasMoreCommandRows()) { + return this.commandRows[this.nextCommandRowIndex++]; + } return null; } - row.cells[2].originalHTML = row.cells[2].innerHTML; - return new SeleniumCommand(getText(row.cells[0]), - getText(row.cells[1]), - getText(row.cells[2])); -} -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() { - currentTest.currentRow.bgColor = workingColor; - scrollIntoView(currentTest.currentRow.cells[0]); - printMetrics(); -} +// TODO: split out an JavascriptTestCase class to handle the "sejs" stuff + +var get_new_rows = function() { + var row_array = new Array(); + for (var i = 0; i < new_block.length; i++) { + + var new_source = (new_block[i][0].tokenizer.source.slice(new_block[i][0].start, + new_block[i][0].end)); -function commandComplete(result) { - if (result.failed) { - numCommandFailures += 1; - recordFailure(result.failureMessage); - } else if (result.passed) { - numCommandPasses += 1; - currentTest.currentRow.bgColor = passColor; - } else { - currentTest.currentRow.bgColor = doneColor; + var row = 'getEval' + + 'currentTest.doNextCommand()' + + '' + new_source + '' + + '' + + row_array.push(row); } -} + return row_array; +}; -function commandError(errorMessage) { - numCommandErrors += 1; - recordFailure(errorMessage); -} -function recordFailure(errorMsg) { - LOG.warn("recordFailure: " + errorMsg); - // Set cell background to red - currentTest.currentRow.bgColor = failColor; +var Metrics = Class.create(); +Object.extend(Metrics.prototype, { + initialize: function() { + // The number of tests run + this.numTestPasses = 0; + // The number of tests that have failed + this.numTestFailures = 0; + // The number of commands which have passed + this.numCommandPasses = 0; + // The number of commands which have failed + this.numCommandFailures = 0; + // The number of commands which have caused errors (element not found) + this.numCommandErrors = 0; + // The time that the test was started. + this.startTime = null; + // The current time. + this.currentTime = null; + }, + + printMetrics: function() { + setText($('commandPasses'), this.numCommandPasses); + setText($('commandFailures'), this.numCommandFailures); + setText($('commandErrors'), this.numCommandErrors); + setText($('testRuns'), this.numTestPasses + this.numTestFailures); + setText($('testFailures'), this.numTestFailures); - // Set error message - currentTest.currentRow.cells[2].innerHTML = errorMsg; - currentTest.currentRow.title = errorMsg; - testFailed = true; - suiteFailed = true; -} + this.currentTime = new Date().getTime(); + + var timeDiff = this.currentTime - this.startTime; + var totalSecs = Math.floor(timeDiff / 1000); + + var minutes = Math.floor(totalSecs / 60); + var seconds = totalSecs % 60; + + setText($('elapsedTime'), this._pad(minutes) + ":" + this._pad(seconds)); + }, -function testComplete() { - var resultColor = passColor; - if (testFailed) { - resultColor = failColor; - numTestFailures += 1; - } else { - numTestPasses += 1; +// Puts a leading 0 on num if it is less than 10 + _pad: function(num) { + return (num > 9) ? num : "0" + num; + }, + + resetMetrics: function() { + this.numTestPasses = 0; + this.numTestFailures = 0; + this.numCommandPasses = 0; + this.numCommandFailures = 0; + this.numCommandErrors = 0; + this.startTime = new Date().getTime(); } - if (currentTest.headerRow) { - currentTest.headerRow.bgColor = resultColor; +}); + +var HtmlRunnerCommandFactory = Class.create(); +Object.extend(HtmlRunnerCommandFactory.prototype, { + + initialize: function(seleniumCommandFactory, testLoop) { + this.seleniumCommandFactory = seleniumCommandFactory; + this.testLoop = testLoop; + this.handlers = { + pause: { + execute: function(selenium, command) { + testLoop.pauseInterval = command.target; + return {}; + } + } + }; + //todo: register commands + }, + + getCommandHandler: function(command) { + if (this.handlers[command]) { + return this.handlers[command]; + } + return this.seleniumCommandFactory.getCommandHandler(command); } - - printMetrics(); - window.setTimeout("runNextTest()", 1); -} +}); -Selenium.prototype.doPause = function(waitTime) { - testLoop.pauseInterval = waitTime; -}; +var HtmlRunnerTestLoop = Class.create(); +Object.extend(HtmlRunnerTestLoop.prototype, new TestLoop()); +Object.extend(HtmlRunnerTestLoop.prototype, { + initialize: function(htmlTestCase, metrics, seleniumCommandFactory) { + + this.commandFactory = new HtmlRunnerCommandFactory(seleniumCommandFactory, this); + this.metrics = metrics; + + this.htmlTestCase = htmlTestCase; + + se = selenium; + global.se = selenium; + + this.currentRow = null; + this.currentRowIndex = 0; + + // used for selenium tests in javascript + this.currentItem = null; + this.commandAgenda = new Array(); + + this.htmlTestCase.reset(); + + this.sejsElement = this.htmlTestCase.testDocument.getElementById('sejs'); + if (this.sejsElement) { + var fname = 'Selenium JavaScript'; + parse_result = parse(this.sejsElement.innerHTML, fname, 0); + + var x2 = new ExecutionContext(GLOBAL_CODE); + ExecutionContext.current = x2; + + execute(parse_result, x2) + } + }, + + _advanceToNextRow: function() { + if (this.htmlTestCase.hasMoreCommandRows()) { + this.currentRow = this.htmlTestCase.getNextCommandRow(); + if (this.sejsElement) { + this.currentItem = agenda.pop(); + this.currentRowIndex++; + } + } else { + this.currentRow = null; + this.currentItem = null; + } + }, + + nextCommand : function() { + this._advanceToNextRow(); + if (this.currentRow == null) { + return null; + } + return this.currentRow.getCommand(); + }, + + commandStarted : function() { + $('pauseTest').disabled = false; + this.currentRow.markWorking(); + this.metrics.printMetrics(); + }, + + commandComplete : function(result) { + if (result.failed) { + this.metrics.numCommandFailures += 1; + this._recordFailure(result.failureMessage); + } else if (result.passed) { + this.metrics.numCommandPasses += 1; + this.currentRow.markPassed(); + } else { + this.currentRow.markDone(); + } + }, + + commandError : function(errorMessage) { + this.metrics.numCommandErrors += 1; + this._recordFailure(errorMessage); + }, + + _recordFailure : function(errorMsg) { + LOG.warn("currentTest.recordFailure: " + errorMsg); + htmlTestRunner.markFailed(); + this.htmlTestCase.addErrorMessage(errorMsg, this.currentRow); + }, + + testComplete : function() { + $('pauseTest').disabled = true; + $('stepTest').disabled = true; + if (htmlTestRunner.testFailed) { + this.htmlTestCase.markFailed(); + this.metrics.numTestFailures += 1; + } else { + this.htmlTestCase.markPassed(); + this.metrics.numTestPasses += 1; + } + + this.metrics.printMetrics(); + + window.setTimeout(function() { + htmlTestRunner.runNextTest(); + }, 1); + }, + + getCommandInterval : function() { + return htmlTestRunner.controlPanel.runInterval; + }, + + pause : function() { + htmlTestRunner.controlPanel.pauseCurrentTest(); + }, + + doNextCommand: function() { + var _n = this.currentItem[0]; + var _x = this.currentItem[1]; + + new_block = new Array() + execute(_n, _x); + if (new_block.length > 0) { + var the_table = this.htmlTestCase.testDocument.getElementById("se-js-table") + var loc = this.currentRowIndex + var new_rows = get_new_rows() + + // make the new statements visible on screen... + for (var i = 0; i < new_rows.length; i++) { + the_table.insertRow(loc + 1); + the_table.rows[loc + 1].innerHTML = new_rows[i]; + this.commandRows.unshift(the_table.rows[loc + 1]) + } + + } + } + +}); -Selenium.prototype.doPause.dontCheckAlertsAndConfirms = true; Selenium.prototype.doBreak = function() { - document.getElementById('modeStep').checked = true; - runInterval = -1; + /** Halt the currently running test, and wait for the user to press the Continue button. + * This command is useful for debugging, but be careful when using it, because it will + * force automated tests to hang until a user intervenes manually. + */ + // todo: should not refer to controlPanel directly + htmlTestRunner.controlPanel.setToPauseAtNextCommand(); }; +Selenium.prototype.doStore = function(expression, variableName) { + /** This command is a synonym for storeExpression. + * @param expression the value to store + * @param variableName the name of a variable in which the result is to be stored. + */ + storedVars[variableName] = expression; +} + /* * Click on the located element, and attach a callback to notify * when the page is reloaded. */ -Selenium.prototype.doModalDialogTest = function(returnValue) { +// DGF TODO this code has been broken for some time... what is it trying to accomplish? +Selenium.prototype.XXXdoModalDialogTest = function(returnValue) { this.browserbot.doModalDialogTest(returnValue); }; -/* - * Store the value of a form input in a variable - */ -Selenium.prototype.doStoreValue = function(target, varName) { - if (!varName) { - // Backward compatibility mode: read the ENTIRE text of the page - // and stores it in a variable with the name of the target - value = this.page().bodyText(); - storedVars[target] = value; - return; +Selenium.prototype.doEcho = function(message) { + /** Prints the specified message into the third table cell in your Selenese tables. + * Useful for debugging. + * @param message the message to print + */ + currentTest.currentRow.setMessage(message); +} + +Selenium.prototype.assertSelected = function(selectLocator, optionLocator) { + /** + * Verifies that the selected option of a drop-down satisfies the optionSpecifier. Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead. + * + *

See the select command for more information about option locators.

+ * + * @param selectLocator an element locator identifying a drop-down menu + * @param optionLocator an option locator, typically just an option label (e.g. "John Smith") + */ + var element = this.page().findElement(selectLocator); + var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); + if (element.selectedIndex == -1) + { + Assert.fail("No option selected"); } - var element = this.page().findElement(target); - storedVars[varName] = getInputValue(element); + locator.assertSelected(element); }; -/* - * Store the text of an element in a variable +/** + * Tell Selenium to expect a failure on the next command execution. This + * command temporarily installs a CommandFactory that generates + * CommandHandlers that expect a failure. */ -Selenium.prototype.doStoreText = function(target, varName) { - var element = this.page().findElement(target); - storedVars[varName] = getText(element); +Selenium.prototype.assertFailureOnNext = function(message) { + if (!message) { + throw new Error("Message must be provided"); + } + + var expectFailureCommandFactory = + new ExpectFailureCommandFactory(currentTest.commandFactory, message, "failure", executeCommandAndReturnFailureMessage); + currentTest.commandFactory = expectFailureCommandFactory; }; -/* - * Store the value of an element attribute in a variable +/** + * Tell Selenium to expect an error on the next command execution. This + * command temporarily installs a CommandFactory that generates + * CommandHandlers that expect a failure. */ -Selenium.prototype.doStoreAttribute = function(target, varName) { - storedVars[varName] = this.page().findAttribute(target); +Selenium.prototype.assertErrorOnNext = function(message) { + if (!message) { + throw new Error("Message must be provided"); + } + + var expectFailureCommandFactory = + new ExpectFailureCommandFactory(currentTest.commandFactory, message, "error", executeCommandAndReturnErrorMessage); + currentTest.commandFactory = expectFailureCommandFactory; }; -/* - * Store the result of a literal value - */ -Selenium.prototype.doStore = function(value, varName) { - storedVars[varName] = value; +function executeCommandAndReturnFailureMessage(baseHandler, originalArguments) { + var baseResult = baseHandler.execute.apply(baseHandler, originalArguments); + if (baseResult.passed) { + return null; + } + return baseResult.failureMessage; +}; + +function executeCommandAndReturnErrorMessage(baseHandler, originalArguments) { + try { + baseHandler.execute.apply(baseHandler, originalArguments); + return null; + } + catch (expected) { + return expected.message; + } }; -Selenium.prototype.doEcho = function(msg) { - currentTest.currentRow.cells[2].innerHTML = msg; +function ExpectFailureCommandHandler(baseHandler, originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor) { + this.execute = function() { + var baseFailureMessage = decoratedExecutor(baseHandler, arguments); + var result = {}; + if (!baseFailureMessage) { + result.failed = true; + result.failureMessage = "Expected " + errorType + " did not occur."; + } + else { + if (! PatternMatcher.matches(expectedErrorMessage, baseFailureMessage)) { + result.failed = true; + result.failureMessage = "Expected " + errorType + " message '" + expectedErrorMessage + + "' but was '" + baseFailureMessage + "'"; + } + else { + result.passed = true; + result.result = baseFailureMessage; + } + } + currentTest.commandFactory = originalCommandFactory; + return result; + }; } + +function ExpectFailureCommandFactory(originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor) { + this.getCommandHandler = function(name) { + var baseHandler = originalCommandFactory.getCommandHandler(name); + return new ExpectFailureCommandHandler(baseHandler, originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor); + }; +}; -- cgit v1.2.3