| /* |
| * Copyright 2011 Software Freedom Conservancy |
| * |
| * 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"; |
| errorColor = "#ffffff"; |
| workingColor = "#DEE7EC"; |
| doneColor = "#FFFFCC"; |
| |
| var injectedSessionId; |
| |
| var postResult = "START"; |
| var debugMode = false; |
| var relayToRC = null; |
| var proxyInjectionMode = false; |
| var uniqueId = 'sel_' + Math.round(100000 * Math.random()); |
| var seleniumSequenceNumber = 0; |
| var cmd8 = ""; |
| var cmd7 = ""; |
| var cmd6 = ""; |
| var cmd5 = ""; |
| var cmd4 = ""; |
| var cmd3 = ""; |
| var cmd2 = ""; |
| var cmd1 = ""; |
| var lastCmd = ""; |
| var lastCmdTime = new Date(); |
| |
| var RemoteRunnerOptions = classCreate(); |
| objectExtend(RemoteRunnerOptions.prototype, URLConfiguration.prototype); |
| objectExtend(RemoteRunnerOptions.prototype, { |
| initialize: function() { |
| this._acquireQueryString(); |
| }, |
| isDebugMode: function() { |
| return this._isQueryParameterTrue("debugMode"); |
| }, |
| |
| getContinue: function() { |
| return this._getQueryParameter("continue"); |
| }, |
| |
| getDriverUrl: function() { |
| return this._getQueryParameter("driverUrl"); |
| }, |
| |
| // requires per-session extension Javascript as soon as this Selenium |
| // instance becomes aware of the session identifier |
| getSessionId: function() { |
| var sessionId = this._getQueryParameter("sessionId"); |
| requireExtensionJs(sessionId); |
| return sessionId; |
| }, |
| |
| _acquireQueryString: function () { |
| if (this.queryString) return; |
| if (browserVersion.isHTA) { |
| var args = this._extractArgs(); |
| if (args.length < 2) return null; |
| this.queryString = args[1]; |
| } else if (proxyInjectionMode) { |
| this.queryString = window.location.search.substr(1); |
| } else { |
| this.queryString = top.location.search.substr(1); |
| } |
| } |
| |
| }); |
| var runOptions; |
| |
| function runSeleniumTest() { |
| runOptions = new RemoteRunnerOptions(); |
| var testAppWindow; |
| |
| if (runOptions.isMultiWindowMode()) { |
| testAppWindow = openSeparateApplicationWindow('Blank.html', true); |
| } else if (sel$('selenium_myiframe') != null) { |
| var myiframe = sel$('selenium_myiframe'); |
| if (myiframe) { |
| testAppWindow = myiframe.contentWindow; |
| } |
| } |
| else { |
| proxyInjectionMode = true; |
| testAppWindow = window; |
| } |
| selenium = Selenium.createForWindow(testAppWindow, proxyInjectionMode); |
| if (runOptions.getBaseUrl()) { |
| selenium.browserbot.baseUrl = runOptions.getBaseUrl(); |
| } |
| if (!debugMode) { |
| debugMode = runOptions.isDebugMode(); |
| } |
| if (proxyInjectionMode) { |
| LOG.logHook = logToRc; |
| selenium.browserbot._modifyWindow(testAppWindow); |
| } |
| else if (debugMode) { |
| LOG.logHook = logToRc; |
| } |
| window.selenium = selenium; |
| |
| commandFactory = new CommandHandlerFactory(); |
| commandFactory.registerAll(selenium); |
| |
| currentTest = new RemoteRunner(commandFactory); |
| |
| var doContinue = runOptions.getContinue(); |
| if (doContinue != null) postResult = "OK"; |
| |
| currentTest.start(); |
| } |
| |
| function buildDriverUrl() { |
| var driverUrl = runOptions.getDriverUrl(); |
| if (driverUrl != null) { |
| return driverUrl; |
| } |
| var s = window.location.href |
| var slashPairOffset = s.indexOf("//") + "//".length |
| var pathSlashOffset = s.substring(slashPairOffset).indexOf("/") |
| return s.substring(0, slashPairOffset + pathSlashOffset) + "/selenium-server/driver/"; |
| //return "http://localhost" + uniqueId + "/selenium-server/driver/"; |
| } |
| |
| function logToRc(logLevel, message) { |
| if (debugMode) { |
| if (logLevel == null) { |
| logLevel = "debug"; |
| } |
| sendToRCAndForget("logLevel=" + logLevel + ":" + message.replace(/[\n\r\015]/g, " ") + "\n", "logging=true"); |
| } |
| } |
| |
| function serializeString(name, s) { |
| return name + "=unescape(\"" + escape(s) + "\");"; |
| } |
| |
| function serializeObject(name, x) |
| { |
| var s = ''; |
| |
| if (isArray(x)) |
| { |
| s = name + "=new Array(); "; |
| var len = x["length"]; |
| for (var j = 0; j < len; j++) |
| { |
| s += serializeString(name + "[" + j + "]", x[j]); |
| } |
| } |
| else if (typeof x == "string") |
| { |
| s = serializeString(name, x); |
| } |
| else |
| { |
| throw "unrecognized object not encoded: " + name + "(" + x + ")"; |
| } |
| return s; |
| } |
| |
| function relayBotToRC(s) { |
| } |
| |
| // seems like no one uses this, but in fact it is called using eval from server-side PI mode code; however, |
| // because multiple names can map to the same popup, assigning a single name confuses matters sometimes; |
| // thus, I'm disabling this for now. -Nelson 10/21/06 |
| function setSeleniumWindowName(seleniumWindowName) { |
| //selenium.browserbot.getCurrentWindow()['seleniumWindowName'] = seleniumWindowName; |
| } |
| |
| RemoteRunner = classCreate(); |
| objectExtend(RemoteRunner.prototype, new TestLoop()); |
| objectExtend(RemoteRunner.prototype, { |
| initialize : function(commandFactory) { |
| this.commandFactory = commandFactory; |
| this.requiresCallBack = true; |
| this.commandNode = null; |
| this.xmlHttpForCommandsAndResults = null; |
| }, |
| |
| nextCommand : function() { |
| var urlParms = ""; |
| if (postResult == "START") { |
| urlParms += "seleniumStart=true"; |
| } |
| this.xmlHttpForCommandsAndResults = XmlHttp.create(); |
| sendToRC(postResult, urlParms, fnBind(this._HandleHttpResponse, this), this.xmlHttpForCommandsAndResults); |
| }, |
| |
| commandStarted : function(command) { |
| this.commandNode = document.createElement("div"); |
| var cmdText = command.command + '('; |
| if (command.target != null && command.target != "") { |
| cmdText += command.target; |
| if (command.value != null && command.value != "") { |
| cmdText += ', ' + command.value; |
| } |
| } |
| if (cmdText.length > 70) { |
| cmdText = cmdText.substring(0, 70) + "...\n"; |
| } else { |
| cmdText += ")\n"; |
| } |
| |
| if (cmdText == lastCmd) { |
| var rightNow = new Date(); |
| var msSinceStart = rightNow.getTime() - lastCmdTime.getTime(); |
| var sinceStart = msSinceStart + "ms"; |
| if (msSinceStart > 1000) { |
| sinceStart = Math.round(msSinceStart / 1000) + "s"; |
| } |
| cmd1 = "Same command (" + sinceStart + "): " + lastCmd; |
| } else { |
| lastCmdTime = new Date(); |
| cmd8 = cmd7; |
| cmd7 = cmd6; |
| cmd6 = cmd5; |
| cmd5 = cmd4; |
| cmd4 = cmd3; |
| cmd3 = cmd2; |
| cmd2 = cmd1; |
| cmd1 = cmdText; |
| } |
| lastCmd = cmdText; |
| |
| if (! proxyInjectionMode) { |
| var commandList = document.commands.commandList; |
| commandList.value = cmd8 + cmd7 + cmd6 + cmd5 + cmd4 + cmd3 + cmd2 + cmd1; |
| commandList.scrollTop = commandList.scrollHeight; |
| } |
| }, |
| |
| commandComplete : function(result) { |
| |
| if (result.failed) { |
| if (postResult == "CONTINUATION") { |
| currentTest.aborted = true; |
| } |
| postResult = result.failureMessage; |
| this.commandNode.title = result.failureMessage; |
| this.commandNode.style.backgroundColor = failColor; |
| } else if (result.passed) { |
| postResult = "OK"; |
| this.commandNode.style.backgroundColor = passColor; |
| } else { |
| if (result.result == null) { |
| postResult = "OK"; |
| } else { |
| var actualResult = result.result; |
| actualResult = selArrayToString(actualResult); |
| postResult = "OK," + actualResult; |
| } |
| this.commandNode.style.backgroundColor = doneColor; |
| } |
| }, |
| |
| commandError : function(message) { |
| postResult = "ERROR: " + message; |
| this.commandNode.style.backgroundColor = errorColor; |
| this.commandNode.title = message; |
| }, |
| |
| testComplete : function() { |
| window.status = "Selenium Tests Complete, for this Test" |
| // Continue checking for new results |
| this.continueTest(); |
| postResult = "START"; |
| }, |
| |
| _HandleHttpResponse : function() { |
| // When request is completed |
| if (this.xmlHttpForCommandsAndResults.readyState == 4) { |
| // OK |
| if (this.xmlHttpForCommandsAndResults.status == 200) { |
| if (this.xmlHttpForCommandsAndResults.responseText=="") { |
| LOG.error("saw blank string xmlHttpForCommandsAndResults.responseText"); |
| return; |
| } |
| var command = this._extractCommand(this.xmlHttpForCommandsAndResults); |
| if (command.command == 'retryLast') { |
| setTimeout(fnBind(function() { |
| sendToRC("RETRY", "retry=true", fnBind(this._HandleHttpResponse, this), this.xmlHttpForCommandsAndResults, true); |
| }, this), 1000); |
| } else { |
| this.currentCommand = command; |
| this.continueTestAtCurrentCommand(); |
| } |
| } |
| // Not OK |
| else { |
| var s = 'xmlHttp returned: ' + this.xmlHttpForCommandsAndResults.status + ": " + this.xmlHttpForCommandsAndResults.statusText; |
| LOG.error(s); |
| this.currentCommand = null; |
| setTimeout(fnBind(this.continueTestAtCurrentCommand, this), 2000); |
| } |
| |
| } |
| }, |
| |
| _extractCommand : function(xmlHttp) { |
| var command, text, json; |
| text = command = xmlHttp.responseText; |
| if (/^json=/.test(text)) { |
| eval(text); |
| if (json.rest) { |
| eval(json.rest); |
| } |
| return json; |
| } |
| try { |
| var re = new RegExp("^(.*?)\n((.|[\r\n])*)"); |
| if (re.exec(xmlHttp.responseText)) { |
| command = RegExp.$1; |
| var rest = RegExp.$2; |
| rest = rest.trim(); |
| if (rest) { |
| eval(rest); |
| } |
| } |
| else { |
| command = xmlHttp.responseText; |
| } |
| } catch (e) { |
| alert('could not get responseText: ' + e.message); |
| } |
| if (command.substr(0, '|testComplete'.length) == '|testComplete') { |
| return null; |
| } |
| |
| return this._createCommandFromRequest(command); |
| }, |
| |
| |
| _delay : function(millis) { |
| var startMillis = new Date(); |
| while (true) { |
| milli = new Date(); |
| if (milli - startMillis > millis) { |
| break; |
| } |
| } |
| }, |
| |
| // Parses a URI query string into a SeleniumCommand object |
| _createCommandFromRequest : function(commandRequest) { |
| //decodeURIComponent doesn't strip plus signs |
| var processed = commandRequest.replace(/\+/g, "%20"); |
| // strip trailing spaces |
| var processed = processed.replace(/\s+$/, ""); |
| var vars = processed.split("&"); |
| var cmdArgs = new Object(); |
| for (var i = 0; i < vars.length; i++) { |
| var pair = vars[i].split("="); |
| cmdArgs[pair[0]] = pair[1]; |
| } |
| var cmd = cmdArgs['cmd']; |
| var arg1 = cmdArgs['1']; |
| if (null == arg1) arg1 = ""; |
| arg1 = decodeURIComponent(arg1); |
| var arg2 = cmdArgs['2']; |
| if (null == arg2) arg2 = ""; |
| arg2 = decodeURIComponent(arg2); |
| if (cmd == null) { |
| throw new Error("Bad command request: " + commandRequest); |
| } |
| return new SeleniumCommand(cmd, arg1, arg2); |
| } |
| |
| }) |
| |
| |
| function sendToRC(dataToBePosted, urlParms, callback, xmlHttpObject, async) { |
| if (async == null) { |
| async = true; |
| } |
| if (xmlHttpObject == null) { |
| xmlHttpObject = XmlHttp.create(); |
| } |
| var url = buildDriverUrl() + "?" |
| if (urlParms) { |
| url += urlParms; |
| } |
| url = addUrlParams(url); |
| url += "&sequenceNumber=" + seleniumSequenceNumber++; |
| |
| var postedData = "postedData=" + encodeURIComponent(dataToBePosted); |
| |
| //xmlHttpObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); |
| xmlHttpObject.open("POST", url, async); |
| if (callback) xmlHttpObject.onreadystatechange = callback; |
| xmlHttpObject.send(postedData); |
| return null; |
| } |
| |
| function addUrlParams(url) { |
| return url + "&localFrameAddress=" + (proxyInjectionMode ? makeAddressToAUTFrame() : "top") |
| + getSeleniumWindowNameURLparameters() |
| + "&uniqueId=" + uniqueId |
| + buildDriverParams() + preventBrowserCaching() |
| } |
| |
| function sendToRCAndForget(dataToBePosted, urlParams) { |
| var url; |
| if (!(browserVersion.isChrome || browserVersion.isHTA)) { |
| // DGF we're behind a proxy, so we can send our logging message to literally any host, to avoid 2-connection limit |
| var protocol = "http:"; |
| if (window.location.protocol == "https:") { |
| // DGF if we're in HTTPS, use another HTTPS url to avoid security warning |
| protocol = "https:"; |
| } |
| // we don't choose a super large random value, but rather 1 - 16, because this matches with the pre-computed |
| // tunnels waiting on the Selenium Server side. This gives us higher throughput than the two-connection-per-host |
| // limitation, but doesn't require we generate an extremely large ammount of fake SSL certs either. |
| url = protocol + "//" + Math.floor(Math.random()* 16 + 1) + ".selenium.doesnotexist/selenium-server/driver/?" + urlParams; |
| } else { |
| url = buildDriverUrl() + "?" + urlParams; |
| } |
| url = addUrlParams(url); |
| |
| var method = "GET"; |
| if (method == "POST") { |
| // DGF submit a request using an iframe; we can't see the response, but we don't need to |
| // TODO not using this mechanism because it screws up back-button |
| var loggingForm = document.createElement("form"); |
| loggingForm.method = "POST"; |
| loggingForm.action = url; |
| loggingForm.target = "seleniumLoggingFrame"; |
| var postedDataInput = document.createElement("input"); |
| postedDataInput.type = "hidden"; |
| postedDataInput.name = "postedData"; |
| postedDataInput.value = dataToBePosted; |
| loggingForm.appendChild(postedDataInput); |
| document.body.appendChild(loggingForm); |
| loggingForm.submit(); |
| document.body.removeChild(loggingForm); |
| } else { |
| var postedData = "&postedData=" + encodeURIComponent(dataToBePosted); |
| var scriptTag = document.createElement("script"); |
| scriptTag.src = url + postedData; |
| document.body.appendChild(scriptTag); |
| document.body.removeChild(scriptTag); |
| } |
| } |
| |
| function buildDriverParams() { |
| var params = ""; |
| |
| var sessionId = runOptions.getSessionId(); |
| if (sessionId == undefined) { |
| sessionId = injectedSessionId; |
| } |
| if (sessionId != undefined) { |
| params = params + "&sessionId=" + sessionId; |
| } |
| return params; |
| } |
| |
| function preventBrowserCaching() { |
| var t = (new Date()).getTime(); |
| return "&counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=" + t; |
| } |
| |
| // |
| // Return URL parameters pertaining to the name(s?) of the current window |
| // |
| // In selenium, the main (i.e., first) window's name is a blank string. |
| // |
| // Additional pop-ups are associated with either 1.) the name given by the 2nd parameter to window.open, or 2.) the name of a |
| // property on the opening window which points at the window. |
| // |
| // An example of #2: if window X contains JavaScript as follows: |
| // |
| // var windowABC = window.open(...) |
| // |
| // Note that the example JavaScript above is equivalent to |
| // |
| // window["windowABC"] = window.open(...) |
| // |
| function getSeleniumWindowNameURLparameters() { |
| var w = (proxyInjectionMode ? selenium.browserbot.getCurrentWindow() : window).top; |
| var s = "&seleniumWindowName="; |
| if (w.opener == null) { |
| return s; |
| } |
| if (w["seleniumWindowName"] == null) { |
| if (w.name) { |
| w["seleniumWindowName"] = w.name; |
| } else { |
| w["seleniumWindowName"] = 'generatedSeleniumWindowName_' + Math.round(100000 * Math.random()); |
| } |
| } |
| s += w["seleniumWindowName"]; |
| var windowOpener = w.opener; |
| for (key in windowOpener) { |
| var val = null; |
| try { |
| val = windowOpener[key]; |
| } |
| catch(e) { |
| } |
| if (val==w) { |
| s += "&jsWindowNameVar=" + key; // found a js variable in the opener referring to this window |
| } |
| } |
| return s; |
| } |
| |
| // construct a JavaScript expression which leads to my frame (i.e., the frame containing the window |
| // in which this code is operating) |
| function makeAddressToAUTFrame(w, frameNavigationalJSexpression) |
| { |
| if (w == null) |
| { |
| w = top; |
| frameNavigationalJSexpression = "top"; |
| } |
| |
| if (w == selenium.browserbot.getCurrentWindow()) |
| { |
| return frameNavigationalJSexpression; |
| } |
| for (var j = 0; j < w.frames.length; j++) |
| { |
| var t = makeAddressToAUTFrame(w.frames[j], frameNavigationalJSexpression + ".frames[" + j + "]"); |
| if (t != null) |
| { |
| return t; |
| } |
| } |
| return null; |
| } |
| |
| Selenium.prototype.doSetContext = function(context) { |
| /** |
| * Writes a message to the status bar and adds a note to the browser-side |
| * log. |
| * |
| * @param context |
| * the message to be sent to the browser |
| */ |
| //set the current test title |
| var ctx = document.getElementById("context"); |
| if (ctx != null) { |
| ctx.innerHTML = context; |
| } |
| }; |
| |
| /** |
| * Adds a script tag referencing a specially-named user extensions "file". The |
| * resource handler for this special file (which won't actually exist) will use |
| * the session ID embedded in its name to retrieve per-session specified user |
| * extension javascript. |
| * |
| * @param sessionId |
| */ |
| function requireExtensionJs(sessionId) { |
| var src = 'scripts/user-extensions.js[' + sessionId + ']'; |
| if (document.getElementById(src) == null) { |
| var scriptTag = document.createElement('script'); |
| scriptTag.language = 'JavaScript'; |
| scriptTag.type = 'text/javascript'; |
| scriptTag.src = src; |
| scriptTag.id = src; |
| var headTag = document.getElementsByTagName('head')[0]; |
| headTag.appendChild(scriptTag); |
| } |
| } |
| |
| Selenium.prototype.doAttachFile = function(fieldLocator,fileLocator) { |
| /** |
| * Sets a file input (upload) field to the file listed in fileLocator |
| * |
| * @param fieldLocator an <a href="#locators">element locator</a> |
| * @param fileLocator a URL pointing to the specified file. Before the file |
| * can be set in the input field (fieldLocator), Selenium RC may need to transfer the file |
| * to the local machine before attaching the file in a web page form. This is common in selenium |
| * grid configurations where the RC server driving the browser is not the same |
| * machine that started the test. |
| * |
| * Supported Browsers: Firefox ("*chrome") only. |
| * |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doCaptureScreenshot = function(filename) { |
| /** |
| * Captures a PNG screenshot to the specified file. |
| * |
| * @param filename the absolute path to the file to be written, e.g. "c:\blah\screenshot.png" |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doCaptureScreenshotToString = function() { |
| /** |
| * Capture a PNG screenshot. It then returns the file as a base 64 encoded string. |
| * |
| * @return string The base 64 encoded string of the screen shot (PNG file) |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doCaptureEntirePageScreenshotToString = function(kwargs) { |
| /** |
| * Downloads a screenshot of the browser current window canvas to a |
| * based 64 encoded PNG file. The <em>entire</em> windows canvas is captured, |
| * including parts rendered outside of the current view port. |
| * |
| * Currently this only works in Mozilla and when running in chrome mode. |
| * |
| * @param kwargs A kwargs string that modifies the way the screenshot is captured. Example: "background=#CCFFDD". This may be useful to set for capturing screenshots of less-than-ideal layouts, for example where absolute positioning causes the calculation of the canvas dimension to fail and a black background is exposed (possibly obscuring black text). |
| * |
| * @return string The base 64 encoded string of the page screenshot (PNG file) |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doShutDownSeleniumServer = function(keycode) { |
| /** |
| * Kills the running Selenium Server and all browser sessions. After you run this command, you will no longer be able to send |
| * commands to the server; you can't remotely start the server once it has been stopped. Normally |
| * you should prefer to run the "stop" command, which terminates the current browser session, rather than |
| * shutting down the entire server. |
| * |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doRetrieveLastRemoteControlLogs = function() { |
| /** |
| * Retrieve the last messages logged on a specific remote control. Useful for error reports, especially |
| * when running multiple remote controls in a distributed environment. The maximum number of log messages |
| * that can be retrieve is configured on remote control startup. |
| * |
| * @return string The last N log messages as a multi-line string. |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doKeyDownNative = function(keycode) { |
| /** |
| * Simulates a user pressing a key (without releasing it yet) by sending a native operating system keystroke. |
| * This function uses the java.awt.Robot class to send a keystroke; this more accurately simulates typing |
| * a key on the keyboard. It does not honor settings from the shiftKeyDown, controlKeyDown, altKeyDown and |
| * metaKeyDown commands, and does not target any particular HTML element. To send a keystroke to a particular |
| * element, focus on the element first before running this command. |
| * |
| * @param keycode an integer keycode number corresponding to a java.awt.event.KeyEvent; note that Java keycodes are NOT the same thing as JavaScript keycodes! |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doKeyUpNative = function(keycode) { |
| /** |
| * Simulates a user releasing a key by sending a native operating system keystroke. |
| * This function uses the java.awt.Robot class to send a keystroke; this more accurately simulates typing |
| * a key on the keyboard. It does not honor settings from the shiftKeyDown, controlKeyDown, altKeyDown and |
| * metaKeyDown commands, and does not target any particular HTML element. To send a keystroke to a particular |
| * element, focus on the element first before running this command. |
| * |
| * @param keycode an integer keycode number corresponding to a java.awt.event.KeyEvent; note that Java keycodes are NOT the same thing as JavaScript keycodes! |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |
| Selenium.prototype.doKeyPressNative = function(keycode) { |
| /** |
| * Simulates a user pressing and releasing a key by sending a native operating system keystroke. |
| * This function uses the java.awt.Robot class to send a keystroke; this more accurately simulates typing |
| * a key on the keyboard. It does not honor settings from the shiftKeyDown, controlKeyDown, altKeyDown and |
| * metaKeyDown commands, and does not target any particular HTML element. To send a keystroke to a particular |
| * element, focus on the element first before running this command. |
| * |
| * @param keycode an integer keycode number corresponding to a java.awt.event.KeyEvent; note that Java keycodes are NOT the same thing as JavaScript keycodes! |
| */ |
| // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us! |
| }; |
| |