Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/.DS_Store =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/.DS_Store ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/css/tests.css =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/css/tests.css (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/css/tests.css (revision 0) @@ -0,0 +1,41 @@ +body{ + height: 100%; + margin: 0; + padding: 0; +} + +html{ + height: 100%; +} + +#activate { + height:3%; +} + +#testFrame{ + height: 97%; + width: 100%; + margin: 0; + padding: 0; + border: none; + display: block; + overflow: auto; +} + +button{ + width: auto; + margin: 0 auto; + display: block; +} + +/* These widths are slightly brittle in that if the width of an individual + thumbnail is changed in Lightbox.css these will need to be updated. */ +.width-6-thumbs { + width: 1050px !important; +} + +.tabs-min-width { + min-width:425px; +} + + Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/.DS_Store =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/.DS_Store ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_browserRunner.js =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_browserRunner.js (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_browserRunner.js (revision 0) @@ -0,0 +1,546 @@ +if(window["dojo"]){ + dojo.provide("doh._browserRunner"); +} + +// FIXME: need to add prompting for monkey-do testing +// FIXME: need to implement progress bar +// FIXME: need to implement errors in progress bar + +(function(){ + if(window.parent == window){ + // we're the top-dog window. + + // borrowed from Dojo, etc. + var byId = function(id){ + return document.getElementById(id); + }; + + var _addOnEvt = function( type, // string + refOrName, // function or string + scope){ // object, defaults is window + + if(!scope){ scope = window; } + + var funcRef = refOrName; + if(typeof refOrName == "string"){ + funcRef = scope[refOrName]; + } + var enclosedFunc = function(){ return funcRef.apply(scope, arguments); }; + + if((window["dojo"])&&(type == "load")){ + dojo.addOnLoad(enclosedFunc); + }else{ + if(window["attachEvent"]){ + window.attachEvent("on"+type, enclosedFunc); + }else if(window["addEventListener"]){ + window.addEventListener(type, enclosedFunc, false); + }else if(document["addEventListener"]){ + document.addEventListener(type, enclosedFunc, false); + } + } + }; + + // + // Over-ride or implement base runner.js-provided methods + // + var escapeXml = function(str){ + //summary: + // Adds escape sequences for special characters in XML: &<>"' + // Optionally skips escapes for single quotes + return str.replace(/&/gm, "&").replace(//gm, ">").replace(/"/gm, """); // string + }; + + var _logBacklog = []; + var sendToLogPane = function(args, skip){ + var msg = ""; + for(var x=0; x "); + if(!byId("logBody")){ + _logBacklog.push(msg); + return; + }else if(_logBacklog.length && !skip){ + var tm; + while((tm=_logBacklog.shift())){ + sendToLogPane(tm, true); + } + } + var tn = document.createElement("div"); + tn.innerHTML = msg; + byId("logBody").appendChild(tn); + } + + doh._init = (function(oi){ + return function(){ + var lb = byId("logBody"); + if(lb){ + // clear the console before each run + while(lb.firstChild){ + lb.removeChild(lb.firstChild); + } + } + this._suiteCount = 0; + oi.apply(doh, arguments); + } + })(doh._init); + + if(this["opera"] && opera.postError){ + doh.debug = function(){ + var msg = ""; + for(var x=0; x
 
"; + tds[3].innerHTML = ""; + + tb.appendChild(tg); + return tg; + } + + var addFixtureToList = function(group, fixture){ + if(!testTemplate){ return; } + var cgn = groupNodes[group]; + if(!cgn["__items"]){ cgn.__items = []; } + var tn = testTemplate.cloneNode(true); + var tds = tn.getElementsByTagName("td"); + + tds[2].innerHTML = fixture.name; + tds[3].innerHTML = ""; + + var nn = (cgn.__lastFixture||cgn.__groupNode).nextSibling; + if(nn){ + nn.parentNode.insertBefore(tn, nn); + }else{ + cgn.__groupNode.parentNode.appendChild(tn); + } + // FIXME: need to make group display toggleable!! + tn.style.display = "none"; + cgn.__items.push(tn); + return (cgn.__lastFixture = tn); + } + + var getFixtureNode = function(group, fixture){ + if(groupNodes[group]){ + return groupNodes[group][fixture.name]; + } + return null; + } + + var getGroupNode = function(group){ + if(groupNodes[group]){ + return groupNodes[group].__groupNode; + } + return null; + } + + var updateBacklog = []; + doh._updateTestList = function(group, fixture, unwindingBacklog){ + if(!loaded){ + if(group && fixture){ + updateBacklog.push([group, fixture]); + } + return; + }else if(updateBacklog.length && !unwindingBacklog){ + var tr; + while((tr=updateBacklog.shift())){ + doh._updateTestList(tr[0], tr[1], true); + } + } + if(group && fixture){ + if(!groupNodes[group]){ + groupNodes[group] = { + "__groupNode": addGroupToList(group) + }; + } + if(!groupNodes[group][fixture.name]){ + groupNodes[group][fixture.name] = addFixtureToList(group, fixture) + } + } + } + + doh._testRegistered = doh._updateTestList; + + doh._groupStarted = function(group){ + if(this._suiteCount == 0){ + this._runedSuite = 0; + this._currentGlobalProgressBarWidth = 0; + this._suiteCount = this._testCount; + } + this._runedSuite++; + // console.debug("_groupStarted", group); + if(doh._inGroup != group){ + doh._totalTime = 0; + doh._runed = 0; + doh._inGroup = group; + } + var gn = getGroupNode(group); + if(gn){ + gn.className = "inProgress"; + } + } + + doh._groupFinished = function(group, success){ + // console.debug("_groupFinished", group); + var gn = getGroupNode(group); + if(gn && doh._inGroup == group){ + gn.className = (success) ? "success" : "failure"; + gn.getElementsByTagName("td")[3].innerHTML = doh._totalTime+"ms"; + gn.getElementsByTagName("td")[2].lastChild.className = ""; + doh._inGroup = null; + //doh._runedSuite++; + doh._updateGlobalProgressBar(this._runedSuite/this._suiteCount,success); + //doh._runedSuite--; + doh._currentGlobalProgressBarWidth = parseInt(this._runedSuite/this._suiteCount*10000)/100; + //byId("progressOuter").style.width = parseInt(this._runedSuite/this._suiteCount*100)+"%"; + } + if(doh._inGroup == group){ + this.debug("Total time for GROUP \"",group,"\" is ",doh._totalTime,"ms"); + } + } + + doh._testStarted = function(group, fixture){ + // console.debug("_testStarted", group, fixture.name); + var fn = getFixtureNode(group, fixture); + if(fn){ + fn.className = "inProgress"; + } + } + + var _nameTimes = {}; + var _playSound = function(name){ + if(byId("hiddenAudio") && byId("audio") && byId("audio").checked){ + // console.debug("playing:", name); + var nt = _nameTimes[name]; + // only play sounds once every second or so + if((!nt)||(((new Date)-nt) > 700)){ + _nameTimes[name] = new Date(); + var tc = document.createElement("span"); + byId("hiddenAudio").appendChild(tc); + tc.innerHTML = ''; + } + } + } + + doh._updateGlobalProgressBar = function(p,success){ + var outerContainer=byId("progressOuter"); + + var gdiv=outerContainer.childNodes[doh._runedSuite-1]; + if(!gdiv){ + gdiv=document.createElement('div'); + outerContainer.appendChild(gdiv); + gdiv.className='success'; + } + if(!success){ + gdiv._failure=true; + gdiv.className='failure'; + } + var tp=parseInt(p*10000)/100; + gdiv.style.width = (tp-doh._currentGlobalProgressBarWidth)+"%"; + return gdiv._failure; + } + doh._testFinished = function(group, fixture, success){ + var fn = getFixtureNode(group, fixture); + var elapsed = fixture.endTime-fixture.startTime; + if(fn){ + fn.getElementsByTagName("td")[3].innerHTML = elapsed+"ms"; + fn.className = (success) ? "success" : "failure"; + + if(!success){ + _playSound("doh"); + var gn = getGroupNode(group); + if(gn){ + gn.className = "failure"; + _getGroupToggler(group)(null, true); + } + } + } + if(doh._inGroup == group){ + var gn = getGroupNode(group); + doh._runed++; + if(gn && doh._curTestCount){ + var p = doh._runed/doh._curTestCount; + var groupfail = this._updateGlobalProgressBar((doh._runedSuite+p-1)/doh._suiteCount,success); + + var pbar = gn.getElementsByTagName("td")[2].lastChild; + pbar.className = groupfail?"failure":"success"; + pbar.style.width = parseInt(p*100)+"%"; + gn.getElementsByTagName("td")[3].innerHTML = parseInt(p*10000)/100+"%"; + } + } + this._totalTime += elapsed; + this.debug((success ? "PASSED" : "FAILED"), "test:", fixture.name, elapsed, 'ms'); + } + + // FIXME: move implementation to _browserRunner? + doh.registerUrl = function( /*String*/ group, + /*String*/ url, + /*Integer*/ timeout){ + var tg = new String(group); + this.register(group, { + name: url, + setUp: function(){ + doh.currentGroupName = tg; + doh.currentGroup = this; + doh.currentUrl = url; + this.d = new doh.Deferred(); + doh.currentTestDeferred = this.d; + showTestPage(); + byId("testBody").src = url; + }, + timeout: timeout||10000, // 10s + // timeout: timeout||1000, // 10s + runTest: function(){ + // FIXME: implement calling into the url's groups here!! + return this.d; + }, + tearDown: function(){ + doh.currentGroupName = null; + doh.currentGroup = null; + doh.currentTestDeferred = null; + doh.currentUrl = null; + // this.d.errback(false); + // byId("testBody").src = "about:blank"; + showLogPage(); + } + }); + } + + // + // Utility code for runner.html + // + // var isSafari = navigator.appVersion.indexOf("Safari") >= 0; + var tabzidx = 1; + var _showTab = function(toShow, toHide){ + // FIXME: I don't like hiding things this way. + byId(toHide).style.display = "none"; + with(byId(toShow).style){ + display = ""; + zIndex = ++tabzidx; + } + } + + showTestPage = function(){ + _showTab("testBody", "logBody"); + } + + showLogPage = function(){ + _showTab("logBody", "testBody"); + } + + var runAll = true; + toggleRunAll = function(){ + // would be easier w/ query...sigh + runAll = !runAll; + if(!byId("testList")){ return; } + var tb = byId("testList").tBodies[0]; + var inputs = tb.getElementsByTagName("input"); + var x=0; var tn; + while((tn=inputs[x++])){ + tn.checked = runAll; + doh._groups[tn.group].skip = (!runAll); + } + } + + var listHeightTimer = null; + var setListHeight = function(){ + if(listHeightTimer){ + clearTimeout(listHeightTimer); + } + var tl = byId("testList"); + if(!tl){ return; } + listHeightTimer = setTimeout(function(){ + tl.style.display = "none"; + tl.style.display = ""; + + }, 10); + } + + _addOnEvt("resize", setListHeight); + _addOnEvt("load", setListHeight); + _addOnEvt("load", function(){ + if(loaded){ return; } + loaded = true; + groupTemplate = byId("groupTemplate"); + if(!groupTemplate){ + // make sure we've got an ammenable DOM structure + return; + } + groupTemplate.parentNode.removeChild(groupTemplate); + groupTemplate.style.display = ""; + testTemplate = byId("testTemplate"); + testTemplate.parentNode.removeChild(testTemplate); + testTemplate.style.display = ""; + doh._updateTestList(); + }); + + _addOnEvt("load", + function(){ + // let robot code run if it gets to this first + var __onEnd = doh._onEnd; + doh._onEnd = function(){ + __onEnd.apply(doh, arguments); + if(doh._failureCount == 0){ + doh.debug("WOOHOO!!"); + _playSound("woohoo"); + }else{ + console.debug("doh._failureCount:", doh._failureCount); + } + if(byId("play")){ + toggleRunning(); + } + } + if(!byId("play")){ + // make sure we've got an amenable DOM structure + return; + } + var isRunning = false; + var toggleRunning = function(){ + // ugg, this would be so much better w/ dojo.query() + if(isRunning){ + byId("play").style.display = byId("pausedMsg").style.display = ""; + byId("playingMsg").style.display = byId("pause").style.display = "none"; + isRunning = false; + }else{ + byId("play").style.display = byId("pausedMsg").style.display = "none"; + byId("playingMsg").style.display = byId("pause").style.display = ""; + isRunning = true; + } + } + doh.run = (function(oldRun){ + return function(){ + if(!doh._currentGroup){ + toggleRunning(); + } + return oldRun.apply(doh, arguments); + } + })(doh.run); + var btns = byId("toggleButtons").getElementsByTagName("span"); + var node; var idx=0; + while((node=btns[idx++])){ + node.onclick = toggleRunning; + } + } + ); + }else{ + // we're in an iframe environment. Time to mix it up a bit. + + _doh = window.parent.doh; + var _thisGroup = _doh.currentGroupName; + var _thisUrl = _doh.currentUrl; + if(_thisGroup){ + doh._testRegistered = function(group, tObj){ + _doh._updateTestList(_thisGroup, tObj); + } + doh._onEnd = function(){ + _doh._errorCount += doh._errorCount; + _doh._failureCount += doh._failureCount; + _doh._testCount += doh._testCount; + // should we be really adding raw group counts? + _doh._groupCount += doh._groupCount; + _doh.currentTestDeferred.callback(true); + } + var otr = doh._getTestObj; + doh._getTestObj = function(){ + var tObj = otr.apply(doh, arguments); + tObj.name = _thisUrl+"::"+arguments[0]+"::"+tObj.name; + return tObj; + } + doh.debug = doh.hitch(_doh, "debug"); + doh.registerUrl = doh.hitch(_doh, "registerUrl"); + doh._testStarted = function(group, fixture){ + _doh._testStarted(_thisGroup, fixture); + } + doh._testFinished = function(g, f, s){ + _doh._testFinished(_thisGroup, f, s); + } + doh._groupStarted = function(g){ + if(!this._setParent){ + _doh._curTestCount = this._testCount; + _doh._curGroupCount = this._groupCount; + this._setParent = true; + } + } + doh._report = function(){}; + } + } + +})(); Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_rhinoRunner.js =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_rhinoRunner.js (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_rhinoRunner.js (revision 0) @@ -0,0 +1,17 @@ +if(this["dojo"]){ + dojo.provide("doh._rhinoRunner"); +} + +doh.debug = print; + +// Override the doh._report method to make it quit with an +// appropriate exit code in case of test failures. +(function(){ + var oldReport = doh._report; + doh._report = function(){ + oldReport.apply(doh, arguments); + if(this._failureCount > 0 || this._errorCount > 0){ + quit(1); + } + } +})(); Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/doh.wav =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/doh.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/dohaaa.wav =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/dohaaa.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/LICENSE =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/LICENSE (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/LICENSE (revision 0) @@ -0,0 +1,10 @@ +License Disclaimer: + +All contents of this directory are Copyright (c) the Dojo Foundation, with the +following exceptions: +------------------------------------------------------------------------------- + +woohoo.wav, doh.wav, dohaaa.wav: + * Copyright original authors. + Copied from: + http://simpson-homer.com/homer-simpson-soundboard.html Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/woohoo.wav =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/_sounds/woohoo.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/LICENSE =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/LICENSE (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/LICENSE (revision 0) @@ -0,0 +1,195 @@ +Dojo is available under *either* the terms of the modified BSD license *or* the +Academic Free License version 2.1. As a recipient of Dojo, you may choose which +license to receive this code under (except as noted in per-module LICENSE +files). Some modules may not be the copyright of the Dojo Foundation. These +modules contain explicit declarations of copyright in both the LICENSE files in +the directories in which they reside and in the code itself. No external +contributions are allowed under licenses which are fundamentally incompatible +with the AFL or BSD licenses that Dojo is distributed under. + +The text of the AFL and BSD licenses is reproduced below. + +------------------------------------------------------------------------------- +The "New" BSD License: +********************** + +Copyright (c) 2005-2008, The Dojo Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the Dojo Foundation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +The Academic Free License, v. 2.1: +********************************** + +This Academic Free License (the "License") applies to any original work of +authorship (the "Original Work") whose owner (the "Licensor") has placed the +following notice immediately following the copyright notice for the Original +Work: + +Licensed under the Academic Free License version 2.1 + +1) Grant of Copyright License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license to do the +following: + +a) to reproduce the Original Work in copies; + +b) to prepare derivative works ("Derivative Works") based upon the Original +Work; + +c) to distribute copies of the Original Work and Derivative Works to the +public; + +d) to perform the Original Work publicly; and + +e) to display the Original Work publicly. + +2) Grant of Patent License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license, under patent +claims owned or controlled by the Licensor that are embodied in the Original +Work as furnished by the Licensor, to make, use, sell and offer for sale the +Original Work and Derivative Works. + +3) Grant of Source Code License. The term "Source Code" means the preferred +form of the Original Work for making modifications to it and all available +documentation describing how to modify the Original Work. Licensor hereby +agrees to provide a machine-readable copy of the Source Code of the Original +Work along with each copy of the Original Work that Licensor distributes. +Licensor reserves the right to satisfy this obligation by placing a +machine-readable copy of the Source Code in an information repository +reasonably calculated to permit inexpensive and convenient access by You for as +long as Licensor continues to distribute the Original Work, and by publishing +the address of that information repository in a notice immediately following +the copyright notice that applies to the Original Work. + +4) Exclusions From License Grant. Neither the names of Licensor, nor the names +of any contributors to the Original Work, nor any of their trademarks or +service marks, may be used to endorse or promote products derived from this +Original Work without express prior written permission of the Licensor. Nothing +in this License shall be deemed to grant any rights to trademarks, copyrights, +patents, trade secrets or any other intellectual property of Licensor except as +expressly stated herein. No patent license is granted to make, use, sell or +offer to sell embodiments of any patent claims other than the licensed claims +defined in Section 2. No right is granted to the trademarks of Licensor even if +such marks are included in the Original Work. Nothing in this License shall be +interpreted to prohibit Licensor from licensing under different terms from this +License any Original Work that Licensor otherwise would have a right to +license. + +5) This section intentionally omitted. + +6) Attribution Rights. You must retain, in the Source Code of any Derivative +Works that You create, all copyright, patent or trademark notices from the +Source Code of the Original Work, as well as any notices of licensing and any +descriptive text identified therein as an "Attribution Notice." You must cause +the Source Code for any Derivative Works that You create to carry a prominent +Attribution Notice reasonably calculated to inform recipients that You have +modified the Original Work. + +7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that +the copyright in and to the Original Work and the patent rights granted herein +by Licensor are owned by the Licensor or are sublicensed to You under the terms +of this License with the permission of the contributor(s) of those copyrights +and patent rights. Except as expressly stated in the immediately proceeding +sentence, the Original Work is provided under this License on an "AS IS" BASIS +and WITHOUT WARRANTY, either express or implied, including, without limitation, +the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. +This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No +license to Original Work is granted hereunder except under this disclaimer. + +8) Limitation of Liability. Under no circumstances and under no legal theory, +whether in tort (including negligence), contract, or otherwise, shall the +Licensor be liable to any person for any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License +or the use of the Original Work including, without limitation, damages for loss +of goodwill, work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses. This limitation of liability shall not +apply to liability for death or personal injury resulting from Licensor's +negligence to the extent applicable law prohibits such limitation. Some +jurisdictions do not allow the exclusion or limitation of incidental or +consequential damages, so this exclusion and limitation may not apply to You. + +9) Acceptance and Termination. If You distribute copies of the Original Work or +a Derivative Work, You must make a reasonable effort under the circumstances to +obtain the express assent of recipients to the terms of this License. Nothing +else but this License (or another written agreement between Licensor and You) +grants You permission to create Derivative Works based upon the Original Work +or to exercise any of the rights granted in Section 1 herein, and any attempt +to do so except under the terms of this License (or another written agreement +between Licensor and You) is expressly prohibited by U.S. copyright law, the +equivalent laws of other countries, and by international treaty. Therefore, by +exercising any of the rights granted to You in Section 1 herein, You indicate +Your acceptance of this License and all of its terms and conditions. + +10) Termination for Patent Action. This License shall terminate automatically +and You may no longer exercise any of the rights granted to You by this License +as of the date You commence an action, including a cross-claim or counterclaim, +against Licensor or any licensee alleging that the Original Work infringes a +patent. This termination provision shall not apply for an action alleging +patent infringement by combinations of the Original Work with other software or +hardware. + +11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this +License may be brought only in the courts of a jurisdiction wherein the +Licensor resides or in which Licensor conducts its primary business, and under +the laws of that jurisdiction excluding its conflict-of-law provisions. The +application of the United Nations Convention on Contracts for the International +Sale of Goods is expressly excluded. Any use of the Original Work outside the +scope of this License or after its termination shall be subject to the +requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et +seq., the equivalent laws of other countries, and international treaty. This +section shall survive the termination of this License. + +12) Attorneys Fees. In any action to enforce the terms of this License or +seeking damages relating thereto, the prevailing party shall be entitled to +recover its costs and expenses, including, without limitation, reasonable +attorneys' fees and costs incurred in connection with such action, including +any appeal of such action. This section shall survive the termination of this +License. + +13) Miscellaneous. This License represents the complete agreement concerning +the subject matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent necessary to +make it enforceable. + +14) Definition of "You" in This License. "You" throughout this License, whether +in upper or lower case, means an individual or a legal entity exercising rights +under, and complying with all of the terms of, this License. For legal +entities, "You" includes any entity that controls, is controlled by, or is +under common control with you. For purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (ii) ownership of fifty percent +(50%) or more of the outstanding shares, or (iii) beneficial ownership of such +entity. + +15) Right to Use. You may use the Original Work in all ways not otherwise +restricted or conditioned by this License or by law, and Licensor promises not +to interfere with or be responsible for such uses by You. + +This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. +Permission is hereby granted to copy and distribute this license without +modification. This license may not be modified without the express written +permission of its copyright owner. Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/README =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/README (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/README (revision 0) @@ -0,0 +1,12 @@ +DOH may be run standalone by issuing a command like the following: + +java -jar ../shrinksafe/custom_rhino.jar runner.js testModule=tests.colors + +where the testModule argument is optional and custom_rhino.jar is just a +convenient copy of the Rhino JavaScript engine -- the custom patch is not +required. + +Optional arguments include: + * dojoUrl - specifies the location of dojo.js + * testUrl - specifies a Javascript file to load with initialization code + * testModule - specifies a test module in the dojo package namespace Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/.DS_Store =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/.DS_Store ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/compilerobot.bat =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/compilerobot.bat (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/compilerobot.bat (revision 0) @@ -0,0 +1,8 @@ +setlocal +del DOHRobot*.class +%JDK14_HOME%\bin\javac -target 1.4 -classpath %JDK14_HOME%\jre\lib\plugin.jar DOHRobot.java +del DOHRobot.jar +%JDK14_HOME%\bin\jar cvf DOHRobot.jar DOHRobot*.class +%JDK14_HOME%\bin\jarsigner -keystore ./dohrobot DOHRobot.jar dojo init Robot"); + try{ + SecurityManager oldsecurity = System.getSecurityManager(); + boolean isOpera = false; + try{ + isOpera = (System.getProperty("browser").equals("Opera.plugin")); + }catch(Exception e){} + try{ + securitymanager = oldsecurity; + securitymanager.checkTopLevelWindow(null); + // xdomain + if(charMap == null){ + if(!confirm("DOH has detected that the current Web page is attempting to access DOH, but belongs to a different domain than the one you agreed to let DOH automate. If you did not intend to start a new DOH test by visiting this Web page, press Cancel now and leave the Web page. Otherwise, press OK to trust this domain to automate DOH tests.")){ + stop(); + return null; + } + } + log("Found old security manager"); + }catch(Exception e){ + e.printStackTrace(); + log("Making new security manager"); + securitymanager = new RobotSecurityManager(isOpera, + oldsecurity); + securitymanager.checkTopLevelWindow(null); + System.setSecurityManager(securitymanager); + } + // instantiate the Robot + robot = new Robot(); + }catch(Exception e){ + log("Error calling _init_: "+e.getMessage()); + key = -2; + e.printStackTrace(); + } + log("< init Robot"); + return null; + } + }); + if(key == -2){ + // applet not trusted + // start the test without it + window.eval("doh.robot._appletDead=true;doh.run();"); + }else{ + // now that the applet has really started, let doh know it's ok to use it + log("_initRobot"); + dohrobot = (JSObject) window.eval("doh.robot"); + dohrobot.call("_initRobot", new Object[]{ applet() }); + } + } + } + + public void init(){ + // ensure isShowing = true + addComponentListener(new onvisible()); + } + + // loading functions + public void _setKey(double key){ + if(key == -1){ + return; + }else if(this.key == -1){ + this.key = key; + } + } + + private boolean isSecure(double key){ + boolean result = this.key != -1 && this.key == key; + log("Key secure: " + result); + return result; + } + + public void _callLoaded(double sec){ + log("> _callLoaded Robot"); + if(!isSecure(sec)){ + return; + } + Thread thread = new Thread(){ + public void run(){ + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + Point p = getLocationOnScreen(); + log("Document root: ~"+p.toString()); + int x = p.x + 16; + int y = p.y + 8; + // click the mouse over the text box + try{ + Thread.sleep(100); + }catch(Exception e){}; + robot.mouseMove(x, y); + try{ + Thread.sleep(100); + }catch(Exception e){}; + robot.mousePress(InputEvent.BUTTON1_MASK); + try{ + Thread.sleep(100); + }catch(Exception e){} + robot.mouseRelease(InputEvent.BUTTON1_MASK); + try{ + Thread.sleep(100); + }catch(Exception e){} + log("< _callLoaded Robot"); + return null; + } + }); + } + }; + thread.start(); + } + + // convenience functions + private DOHRobot applet(){ + return this; + } + + public void log(final String s){ + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + System.out.println((new Date()).toString() + ": " + s); + return null; + } + }); + } + + private void alert(final String s){ + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + window.eval("top.alert(\"" + s + "\");"); + return null; + } + }); + } + + private boolean confirm(final String s){ + return ((Boolean) AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + return ((Boolean) window.eval("top.confirm(\"" + s + "\");")); + } + })).booleanValue(); + } + + // mouse discovery code + public void setDocumentBounds(double sec, int x, int y, int w, int h){ + log("> setDocumentBounds"); + if(!isSecure(sec)) + return; + // call from JavaScript + // tells the Robot where the screen x,y of the upper left corner of the + // document are + // not screenX/Y of the window; really screenLeft/Top in IE, but not all + // browsers have this + if(!inited){ + inited = true; + this.docScreenX = x; + this.docScreenY = y; + this.docScreenXMax = x + w; + this.docScreenYMax = y + h; + } + log("< setDocumentBounds"); + } + + // keyboard discovery code + private void _mapKey(char charCode, int keyindex, boolean shift, + boolean altgraph){ + log("_mapKey: " + charCode); + // if character is not in map, add it + if(!charMap.containsKey(new Integer(charCode))){ + log("Notified: " + (char) charCode); + KeyEvent event = new KeyEvent(applet(), 0, 0, + (shift ? KeyEvent.SHIFT_MASK : 0) + + (altgraph ? KeyEvent.ALT_GRAPH_MASK : 0), + ((Integer) vkKeys.get(keyindex)).intValue(), + (char) charCode); + charMap.put(new Integer(charCode), event); + log("Mapped char " + (char) charCode + " to KeyEvent " + event); + if(((char) charCode) >= 'a' && ((char) charCode) <= 'z'){ + // put shifted version of a-z in automatically + int uppercharCode = (int) Character + .toUpperCase((char) charCode); + event = new KeyEvent(applet(), 0, 0, KeyEvent.SHIFT_MASK + + (altgraph ? KeyEvent.ALT_GRAPH_MASK : 0), + ((Integer) vkKeys.get(keyindex)).intValue(), + (char) uppercharCode); + charMap.put(new Integer(uppercharCode), event); + log("Mapped char " + (char) uppercharCode + " to KeyEvent " + + event); + } + } + } + + public void _notified(double sec, final String chars){ + if(!isSecure(sec)) + return; + // decouple from JavaScript; thread join could hang it + Thread thread = new Thread("_notified"){ + public void run(){ + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + try{ + // wait for release shift/altgraph to resolve + if(previousThread != null){ + previousThread.join(); + } + }catch(Exception e){ + } + keystring += chars; + if(altgraph && !shift){ + shift = false; + // Set robot auto delay now that FF/Mac inited all of the keys. + // Good for DND. + robot.setAutoDelay(1); + try{ + log(keystring); + int index = 0; + for (int i = 0; (i < vkKeys.size()) + && (index < keystring.length()); i++){ + char c = keystring.charAt(index++); + _mapKey(c, i, false, false); + } + for (int i = 0; (i < vkKeys.size()) + && (index < keystring.length()); i++){ + char c = keystring.charAt(index++); + _mapKey(c, i, true, false); + } + for (int i = 0; (i < vkKeys.size()) + && (index < keystring.length()); i++){ + char c = keystring.charAt(index++); + _mapKey(c, i, false, true); + } + // notify DOH that the applet finished init + dohrobot.call("_onKeyboard", new Object[]{}); + }catch(Exception e){ + e.printStackTrace(); + } + return null; + }else if(!shift){ + shift = true; + }else{ + shift = false; + altgraph = true; + } + pressNext(); + // } + return null; + } + }); + } + }; + thread.start(); + } + + private void pressNext(){ + final Thread myPreviousThread = previousThread; + Thread thread = new Thread("pressNext"){ + public void run(){ + try{ + // wait for release shift/altgraph to resolve + if(myPreviousThread != null){ + myPreviousThread.join(); + } + }catch(Exception e){ + } + // first time, press shift (have to do it here instead of + // _notified to avoid IllegalThreadStateException on Mac) + log("starting up, " + shift + " " + altgraph); + if(shift){ + robot.keyPress(KeyEvent.VK_SHIFT); + log("Pressing shift"); + } + try{ + if(altgraph){ + robot.keyPress(KeyEvent.VK_ALT_GRAPH); + log("Pressing alt graph"); + } + }catch(Exception e){ + log("Error pressing alt graph"); + e.printStackTrace(); + _notified(key, ""); + return; + } + dohrobot.call("_nextKeyGroup", new Object[]{ new Integer(vkKeys.size()) }); + for (int keyindex = 0; keyindex < vkKeys.size(); keyindex++){ + try{ + log("Press " + + ((Integer) vkKeys.get(keyindex)).intValue()); + robot.keyPress(((Integer) vkKeys.get(keyindex)) + .intValue()); + log("Release " + + ((Integer) vkKeys.get(keyindex)).intValue()); + robot.keyRelease(((Integer) vkKeys.get(keyindex)) + .intValue()); + if(altgraph && (keyindex == (vkKeys.size() - 1))){ + robot.keyRelease(KeyEvent.VK_ALT_GRAPH); + log("Releasing alt graph"); + } + if(shift && (keyindex == (vkKeys.size() - 1))){ + robot.keyRelease(KeyEvent.VK_SHIFT); + log("Releasing shift"); + } + }catch(Exception e){ + } + try{ + log("Press space"); + robot.keyPress(KeyEvent.VK_SPACE); + log("Release space"); + robot.keyRelease(KeyEvent.VK_SPACE); + }catch(Exception e){ + e.printStackTrace(); + } + } + } + }; + previousThread = thread; + thread.start(); + } + + public void _initWheel(double sec){ + log("> initWheel"); + if(!isSecure(sec)) + return; + Thread thread=new Thread(){ + public void run(){ + // calibrate the mouse wheel now that textbox is focused + int dir=1; + if(System.getProperty("os.name").toUpperCase().indexOf("MAC") != -1){ + dir=-1; + } + robot.mouseWheel(dir); + try{ + Thread.sleep(100); + }catch(Exception e){} + log("< initWheel"); + } + }; + thread.start(); + } + + public void _initKeyboard(double sec){ + log("> initKeyboard"); + // javascript entry point to discover the keyboard + if(!isSecure(sec)) + return; + if(charMap != null){ + dohrobot.call("_onKeyboard", new Object[]{}); + return; + } + + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + charMap = new HashMap(); + KeyEvent event = new KeyEvent(applet(), 0, 0, 0, + KeyEvent.VK_SPACE, ' '); + charMap.put(new Integer(32), event); + try{ + // a-zA-Z0-9 + 29 others + vkKeys = new Vector(); + for (char i = 'a'; i <= 'z'; i++){ + vkKeys.add(new Integer(KeyEvent.class.getField( + "VK_" + Character.toUpperCase((char) i)) + .getInt(null))); + } + for (char i = '0'; i <= '9'; i++){ + vkKeys.add(new Integer(KeyEvent.class.getField( + "VK_" + Character.toUpperCase((char) i)) + .getInt(null))); + } + int[] mykeys = new int[]{ KeyEvent.VK_COMMA, + KeyEvent.VK_MINUS, KeyEvent.VK_PERIOD, + KeyEvent.VK_SLASH, KeyEvent.VK_SEMICOLON, + KeyEvent.VK_LEFT_PARENTHESIS, + KeyEvent.VK_NUMBER_SIGN, KeyEvent.VK_PLUS, + KeyEvent.VK_RIGHT_PARENTHESIS, + KeyEvent.VK_UNDERSCORE, + KeyEvent.VK_EXCLAMATION_MARK, KeyEvent.VK_DOLLAR, + KeyEvent.VK_CIRCUMFLEX, KeyEvent.VK_AMPERSAND, + KeyEvent.VK_ASTERISK, KeyEvent.VK_QUOTEDBL, + KeyEvent.VK_LESS, KeyEvent.VK_GREATER, + KeyEvent.VK_BRACELEFT, KeyEvent.VK_BRACERIGHT, + KeyEvent.VK_COLON, KeyEvent.VK_BACK_QUOTE, + KeyEvent.VK_QUOTE, KeyEvent.VK_OPEN_BRACKET, + KeyEvent.VK_BACK_SLASH, KeyEvent.VK_CLOSE_BRACKET, + KeyEvent.VK_EQUALS }; + for (int i = 0; i < mykeys.length; i++){ + vkKeys.add(new Integer(mykeys[i])); + } + }catch(Exception e){ + e.printStackTrace(); + } + Thread thread = new Thread(){ + public void run(){ + robot.setAutoDelay(0); + log("< initKeyboard"); + pressNext(); + } + }; + thread.start(); + return null; + } + }); + } + + public void typeKey(double sec, final int charCode, final int keyCode, + final boolean alt, final boolean ctrl, final boolean shift, + final int delay, final boolean async){ + if(!isSecure(sec)) + return; + // called by doh.robot._keyPress + // see it for details + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + try{ + log("> typeKey Robot " + charCode + ", " + keyCode + ", " + async); + KeyPressThread thread = new KeyPressThread(charCode, + keyCode, alt, ctrl, shift, delay, async?null:previousThread); + previousThread = async?previousThread:thread; + thread.start(); + log("< typeKey Robot"); + }catch(Exception e){ + log("Error calling typeKey"); + e.printStackTrace(); + } + return null; + } + }); + } + + public void upKey(double sec, final int charCode, final int keyCode, final int delay){ + // called by doh.robot.keyDown + // see it for details + // a nice name like "keyUp" is reserved in Java + if(!isSecure(sec)) + return; + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + log("> upKey Robot " + charCode + ", " + keyCode); + KeyUpThread thread = new KeyUpThread(charCode, keyCode, delay, previousThread); + previousThread = thread; + thread.start(); + log("< upKey Robot"); + return null; + } + }); + } + + public void downKey(double sec, final int charCode, final int keyCode, final int delay){ + // called by doh.robot.keyUp + // see it for details + // a nice name like "keyDown" is reserved in Java + if(!isSecure(sec)) + return; + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + log("> downKey Robot " + charCode + ", " + keyCode); + KeyDownThread thread = new KeyDownThread(charCode, keyCode, delay, previousThread); + previousThread = thread; + thread.start(); + log("< downKey Robot"); + return null; + } + }); + } + + public void pressMouse(double sec, final boolean left, + final boolean middle, final boolean right, final int delay){ + if(!isSecure(sec)) + return; + // called by doh.robot.mousePress + // see it for details + // a nice name like "mousePress" is reserved in Java + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + log("> mousePress Robot " + left + ", " + middle + ", " + right); + MousePressThread thread = new MousePressThread( + (left ? InputEvent.BUTTON1_MASK : 0) + + (middle ? InputEvent.BUTTON2_MASK : 0) + + (right ? InputEvent.BUTTON3_MASK : 0), delay, + previousThread); + previousThread = thread; + thread.start(); + log("< mousePress Robot"); + return null; + } + }); + } + + public void releaseMouse(double sec, final boolean left, + final boolean middle, final boolean right, final int delay){ + if(!isSecure(sec)) + return; + // called by doh.robot.mouseRelease + // see it for details + // a nice name like "mouseRelease" is reserved in Java + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + log("> mouseRelease Robot " + left + ", " + middle + ", " + + right); + MouseReleaseThread thread = new MouseReleaseThread( + (left ? InputEvent.BUTTON1_MASK : 0) + + (middle ? InputEvent.BUTTON2_MASK : 0) + + (right ? InputEvent.BUTTON3_MASK : 0), delay, + previousThread); + previousThread = thread; + thread.start(); + log("< mouseRelease Robot"); + return null; + } + }); + } + + public void moveMouse(double sec, final int x1, final int y1, final int d, final int duration){ + // called by doh.robot.mouseMove + // see it for details + // a nice name like "mouseMove" is reserved in Java + if(!isSecure(sec)) + return; + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + int x = x1 + docScreenX; + int y = y1 + docScreenY; + if(x > docScreenXMax || y > docScreenYMax){ + // TODO: try to scroll view + log("Request to mouseMove denied"); + return null; + } + int delay = d; + log("> mouseMove Robot " + x + ", " + y); + MouseMoveThread thread = new MouseMoveThread(x, y, delay, + duration, previousThread); + previousThread = thread; + thread.start(); + log("< mouseMove Robot"); + return null; + } + }); + } + + public void wheelMouse(double sec, final int amount, final int delay, final int duration){ + // called by doh.robot.mouseWheel + // see it for details + if(!isSecure(sec)) + return; + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + MouseWheelThread thread = new MouseWheelThread(amount, delay, duration, + previousThread); + previousThread = thread; + thread.start(); + return null; + } + }); + } + + private int getVKCode(int charCode, int keyCode){ + int keyboardCode = 0; + if(charCode >= 32){ + // if it is printable, then it lives in our hashmap + KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode)); + keyboardCode = event.getKeyCode(); + } + else{ + switch (keyCode){ + case 13: + keyboardCode = KeyEvent.VK_ENTER; + break; + case 8: + keyboardCode = KeyEvent.VK_BACK_SPACE; + break; + case 25:// shift tab for Safari + case 9: + keyboardCode = KeyEvent.VK_TAB; + break; + case 12: + keyboardCode = KeyEvent.VK_CLEAR; + break; + case 16: + keyboardCode = KeyEvent.VK_SHIFT; + break; + case 17: + keyboardCode = KeyEvent.VK_CONTROL; + break; + case 18: + keyboardCode = KeyEvent.VK_ALT; + break; + case 63250: + case 19: + keyboardCode = KeyEvent.VK_PAUSE; + break; + case 20: + keyboardCode = KeyEvent.VK_CAPS_LOCK; + break; + case 27: + keyboardCode = KeyEvent.VK_ESCAPE; + break; + case 32: + log("it's a space"); + keyboardCode = KeyEvent.VK_SPACE; + break; + case 63276: + case 33: + keyboardCode = KeyEvent.VK_PAGE_UP; + break; + case 63277: + case 34: + keyboardCode = KeyEvent.VK_PAGE_DOWN; + break; + case 63275: + case 35: + keyboardCode = KeyEvent.VK_END; + break; + case 63273: + case 36: + keyboardCode = KeyEvent.VK_HOME; + break; + + /** + * Constant for the left arrow key. + */ + case 63234: + case 37: + keyboardCode = KeyEvent.VK_LEFT; + break; + + /** + * Constant for the up arrow key. + */ + case 63232: + case 38: + keyboardCode = KeyEvent.VK_UP; + break; + + /** + * Constant for the right arrow key. + */ + case 63235: + case 39: + keyboardCode = KeyEvent.VK_RIGHT; + break; + + /** + * Constant for the down arrow key. + */ + case 63233: + case 40: + keyboardCode = KeyEvent.VK_DOWN; + break; + case 63272: + case 46: + keyboardCode = KeyEvent.VK_DELETE; + break; + case 63289: + case 144: + keyboardCode = KeyEvent.VK_NUM_LOCK; + break; + case 63249: + case 145: + keyboardCode = KeyEvent.VK_SCROLL_LOCK; + break; + + /** Constant for the F1 function key. */ + case 63236: + case 112: + keyboardCode = KeyEvent.VK_F1; + break; + + /** Constant for the F2 function key. */ + case 63237: + case 113: + keyboardCode = KeyEvent.VK_F2; + break; + + /** Constant for the F3 function key. */ + case 63238: + case 114: + keyboardCode = KeyEvent.VK_F3; + break; + + /** Constant for the F4 function key. */ + case 63239: + case 115: + keyboardCode = KeyEvent.VK_F4; + break; + + /** Constant for the F5 function key. */ + case 63240: + case 116: + keyboardCode = KeyEvent.VK_F5; + break; + + /** Constant for the F6 function key. */ + case 63241: + case 117: + keyboardCode = KeyEvent.VK_F6; + break; + + /** Constant for the F7 function key. */ + case 63242: + case 118: + keyboardCode = KeyEvent.VK_F7; + break; + + /** Constant for the F8 function key. */ + case 63243: + case 119: + keyboardCode = KeyEvent.VK_F8; + break; + + /** Constant for the F9 function key. */ + case 63244: + case 120: + keyboardCode = KeyEvent.VK_F9; + break; + + /** Constant for the F10 function key. */ + case 63245: + case 121: + keyboardCode = KeyEvent.VK_F10; + break; + + /** Constant for the F11 function key. */ + case 63246: + case 122: + keyboardCode = KeyEvent.VK_F11; + break; + + /** Constant for the F12 function key. */ + case 63247: + case 123: + keyboardCode = KeyEvent.VK_F12; + break; + + /** + * Constant for the F13 function key. + * + * @since 1.2 + */ + /* + * F13 - F24 are used on IBM 3270 keyboard; break; use + * random range for constants. + */ + case 124: + keyboardCode = KeyEvent.VK_F13; + break; + + /** + * Constant for the F14 function key. + * + * @since 1.2 + */ + case 125: + keyboardCode = KeyEvent.VK_F14; + break; + + /** + * Constant for the F15 function key. + * + * @since 1.2 + */ + case 126: + keyboardCode = KeyEvent.VK_F15; + break; + + case 63302: + case 45: + keyboardCode = KeyEvent.VK_INSERT; + break; + case 47: + keyboardCode = KeyEvent.VK_HELP; + break; + + } + } + log("Attempting to type " + (char) charCode + ":" + + charCode + " " + keyCode); + log("Converted to " + keyboardCode); + return keyboardCode; + } + + private boolean isUnsafe(int keyboardCode){ + // run through exemption list + log("ctrl: "+ctrl+", alt: "+alt+", shift: "+shift); + if(((ctrl || alt) && keyboardCode == KeyEvent.VK_ESCAPE) + || (alt && keyboardCode == KeyEvent.VK_TAB) + || (ctrl && alt && keyboardCode == KeyEvent.VK_DELETE)){ + log("You are not allowed to press this key combination!"); + return true; + }else{ + log("Safe to press."); + return false; + } + } + + private void _typeKey(final int cCode, final int kCode, final boolean a, + final boolean c, final boolean s){ + AccessController.doPrivileged(new PrivilegedAction(){ + public Object run(){ + int charCode = cCode; + int keyCode = kCode; + boolean alt = a; + boolean ctrl = c; + boolean shift = s; + boolean altgraph = false; + log("> _typeKey Robot " + charCode + ", " + keyCode); + try{ + int keyboardCode=getVKCode(charCode, keyCode); + if(charCode >= 32){ + // if it is printable, then it lives in our hashmap + KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode)); + // see if we need to press shift to generate this + // character + if(!shift){ + shift = event.isShiftDown(); + } + altgraph = event.isAltGraphDown(); + keyboardCode = event.getKeyCode(); + } + + // run through exemption list + if(!isUnsafe(keyboardCode)){ + if(shift){ + log("Pressing shift"); + robot.keyPress(KeyEvent.VK_SHIFT); + } + if(alt){ + log("Pressing alt"); + robot.keyPress(KeyEvent.VK_ALT); + } + if(altgraph){ + log("Pressing altgraph"); + robot.keyPress(KeyEvent.VK_ALT_GRAPH); + } + if(ctrl){ + log("Pressing ctrl"); + robot.keyPress(KeyEvent.VK_CONTROL); + } + if(keyboardCode != KeyEvent.VK_SHIFT + && keyboardCode != KeyEvent.VK_ALT + && keyboardCode != KeyEvent.VK_CONTROL){ + try{ + robot.keyPress(keyboardCode); + robot.keyRelease(keyboardCode); + }catch(Exception e){ + log("Error while actually typing a key"); + e.printStackTrace(); + } + + } + if(ctrl){ + robot.keyRelease(KeyEvent.VK_CONTROL); + ctrl = false; + } + if(alt){ + robot.keyRelease(KeyEvent.VK_ALT); + alt = false; + } + if(altgraph){ + robot.keyRelease(KeyEvent.VK_ALT_GRAPH); + altgraph = false; + } + if(shift){ + log("Releasing shift"); + robot.keyRelease(KeyEvent.VK_SHIFT); + shift = false; + } + } + }catch(Exception e){ + log("Error in _typeKey"); + e.printStackTrace(); + } + log("< _typeKey Robot"); + return null; + } + }); + } + + public boolean hasFocus(){ + try{ + return ((Boolean) window + .eval("var result=false;if(window.parent.document.hasFocus){result=window.parent.document.hasFocus();}else{result=true;}result;")) + .booleanValue(); + }catch(Exception e){ + // runs even after you close the window! + return false; + } + } + + // Threads for common Robot tasks + // (so as not to tie up the browser rendering thread!) + // declared inside so they have private access to the robot + // we do *not* want to expose that guy! + final private class KeyPressThread extends Thread{ + private int charCode; + private int keyCode; + private boolean alt; + private boolean ctrl; + private boolean shift; + private int delay; + private Thread myPreviousThread = null; + + public KeyPressThread(int charCode, int keyCode, boolean alt, + boolean ctrl, boolean shift, int delay, Thread myPreviousThread){ + log("KeyPressThread constructor " + charCode + ", " + keyCode); + this.charCode = charCode; + this.keyCode = keyCode; + this.alt = alt; + this.ctrl = ctrl; + this.shift = shift; + this.delay = delay; + this.myPreviousThread = myPreviousThread; + } + + public void run(){ + try{ + if(myPreviousThread != null) + myPreviousThread.join(); + // in different order so async works + while(!hasFocus()){ + Thread.sleep(1000); + } + Thread.sleep(delay); + log("> run KeyPressThread"); + + _typeKey(charCode, keyCode, alt, ctrl, shift); + }catch(Exception e){ + log("Bad parameters passed to _typeKey"); + e.printStackTrace(); + } + log("< run KeyPressThread"); + + } + } + + final private class KeyDownThread extends Thread{ + private int charCode; + private int keyCode; + private int delay; + private Thread myPreviousThread = null; + + public KeyDownThread(int charCode, int keyCode, int delay, Thread myPreviousThread){ + log("KeyDownThread constructor " + charCode + ", " + keyCode); + this.charCode = charCode; + this.keyCode = keyCode; + this.delay = delay; + this.myPreviousThread = myPreviousThread; + } + + public void run(){ + try{ + if(myPreviousThread != null) + myPreviousThread.join(); + Thread.sleep(delay); + log("> run KeyDownThread"); + while(!hasFocus()){ + Thread.sleep(1000); + } + int vkCode=getVKCode(charCode, keyCode); + if(charCode >= 32){ + // if it is printable, then it lives in our hashmap + KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode)); + // see if we need to press shift to generate this + // character + if(event.isShiftDown()){ + robot.keyPress(KeyEvent.VK_SHIFT); + shift=true; + } + if(event.isAltGraphDown()){ + robot.keyPress(KeyEvent.VK_ALT_GRAPH); + altgraph=true; + } + }else{ + if(vkCode==KeyEvent.VK_ALT){ + alt=true; + }else if(vkCode==KeyEvent.VK_CONTROL){ + ctrl=true; + }else if(vkCode==KeyEvent.VK_SHIFT){ + shift=true; + }else if(vkCode==KeyEvent.VK_ALT_GRAPH){ + altgraph=true; + } + } + if(!isUnsafe(vkCode)){ + robot.keyPress(vkCode); + } + }catch(Exception e){ + log("Bad parameters passed to downKey"); + e.printStackTrace(); + } + log("< run KeyDownThread"); + + } + } + + final private class KeyUpThread extends Thread{ + private int charCode; + private int keyCode; + private int delay; + private Thread myPreviousThread = null; + + public KeyUpThread(int charCode, int keyCode, int delay, Thread myPreviousThread){ + log("KeyUpThread constructor " + charCode + ", " + keyCode); + this.charCode = charCode; + this.keyCode = keyCode; + this.delay = delay; + this.myPreviousThread = myPreviousThread; + } + + public void run(){ + try{ + if(myPreviousThread != null) + myPreviousThread.join(); + Thread.sleep(delay); + log("> run KeyUpThread"); + while(!hasFocus()){ + Thread.sleep(1000); + } + int vkCode=getVKCode(charCode, keyCode); + if(charCode >= 32){ + // if it is printable, then it lives in our hashmap + KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode)); + // see if we need to press shift to generate this + // character + if(event.isShiftDown()){ + robot.keyRelease(KeyEvent.VK_SHIFT); + shift=false; + } + if(event.isAltGraphDown()){ + robot.keyRelease(KeyEvent.VK_ALT_GRAPH); + altgraph=false; + } + }else{ + if(vkCode==KeyEvent.VK_ALT){ + alt=false; + }else if(vkCode==KeyEvent.VK_CONTROL){ + ctrl=false; + }else if(vkCode==KeyEvent.VK_SHIFT){ + shift=false; + }else if(vkCode==KeyEvent.VK_ALT_GRAPH){ + altgraph=false; + } + } + robot.keyRelease(vkCode); + }catch(Exception e){ + log("Bad parameters passed to upKey"); + e.printStackTrace(); + } + log("< run KeyUpThread"); + + } + } + + final private class MousePressThread extends Thread{ + private int mask; + private int delay; + private Thread myPreviousThread = null; + + public MousePressThread(int mask, int delay, Thread myPreviousThread){ + this.mask = mask; + this.delay = delay; + this.myPreviousThread = myPreviousThread; + } + + public void run(){ + try{ + if(myPreviousThread != null) + myPreviousThread.join(); + Thread.sleep(delay); + log("> run MousePressThread"); + while(!hasFocus()){ + Thread.sleep(1000); + } + robot.mousePress(mask); + robot.waitForIdle(); + }catch(Exception e){ + log("Bad parameters passed to mousePress"); + e.printStackTrace(); + } + log("< run MousePressThread"); + + } + } + + final private class MouseReleaseThread extends Thread{ + private int mask; + private int delay; + private Thread myPreviousThread = null; + + public MouseReleaseThread(int mask, int delay, Thread myPreviousThread){ + this.mask = mask; + this.delay = delay; + this.myPreviousThread = myPreviousThread; + } + + public void run(){ + try{ + if(myPreviousThread != null) + myPreviousThread.join(); + Thread.sleep(delay); + log("> run MouseReleaseThread "); + while(!hasFocus()){ + Thread.sleep(1000); + } + robot.mouseRelease(mask); + robot.waitForIdle(); + }catch(Exception e){ + log("Bad parameters passed to mouseRelease"); + e.printStackTrace(); + } + + log("< run MouseReleaseThread "); + + } + } + + final private class MouseMoveThread extends Thread{ + private int x; + private int y; + private int delay; + private int duration; + private Thread myPreviousThread = null; + + public MouseMoveThread(int x, int y, int delay, int duration, Thread myPreviousThread){ + this.x = x; + this.y = y; + this.delay = delay; + this.duration = duration; + this.myPreviousThread = myPreviousThread; + } + + public double easeInOutQuad(double t, double b, double c, double d){ + t /= d / 2; + if(t < 1) + return c / 2 * t * t + b; + t--; + return -c / 2 * (t * (t - 2) - 1) + b; + }; + + public void run(){ + try{ + if(myPreviousThread != null) + myPreviousThread.join(); + Thread.sleep(delay); + log("> run MouseMoveThread " + x + ", " + y); + while(!hasFocus()){ + Thread.sleep(1000); + } + int x1 = lastMouseX; + int x2 = x; + int y1 = lastMouseY; + int y2 = y; + // shrink range by 1 px on both ends + // manually move this 1px to trip DND code + if(x1 != x2){ + int dx = x - lastMouseX; + if(dx > 0){ + x1 += 1; + x2 -= 1; + }else{ + x1 -= 1; + x2 += 1; + } + } + if(y1 != y2){ + int dy = y - lastMouseY; + if(dy > 0){ + y1 += 1; + y2 -= 1; + }else{ + y1 -= 1; + y2 += 1; + } + + } + robot.setAutoDelay(Math.max(duration/100,1)); + robot.mouseMove(x1, y1); + int d = 100; + for (int t = 0; t <= d; t++){ + x1 = (int) easeInOutQuad((double) t, (double) lastMouseX, + (double) x2 - lastMouseX, (double) d); + y1 = (int) easeInOutQuad((double) t, (double) lastMouseY, + (double) y2 - lastMouseY, (double) d); + robot.mouseMove(x1, y1); + } + robot.mouseMove(x, y); + lastMouseX = x; + lastMouseY = y; + robot.waitForIdle(); + robot.setAutoDelay(1); + }catch(Exception e){ + log("Bad parameters passed to mouseMove"); + e.printStackTrace(); + } + + log("< run MouseMoveThread"); + + } + } + + final private class MouseWheelThread extends Thread{ + private int amount; + private int delay; + private int duration; + private Thread myPreviousThread = null; + + public MouseWheelThread(int amount, int delay, int duration, Thread myPreviousThread){ + this.amount = amount; + this.delay = delay; + this.duration = duration; + this.myPreviousThread = myPreviousThread; + } + + public void run(){ + try{ + if(myPreviousThread != null) + myPreviousThread.join(); + Thread.sleep(delay); + log("> run MouseWheelThread " + amount); + while(!hasFocus()){ + Thread.sleep(1000); + } + int dir = 1; + if(System.getProperty("os.name").toUpperCase().indexOf("MAC") != -1){ + // yay for Apple + dir = -1; + } + robot.setAutoDelay(Math.max(duration/Math.abs(amount),1)); + for(int i=0; i0?dir:-dir); + } + robot.setAutoDelay(1); + }catch(Exception e){ + log("Bad parameters passed to mouseWheel"); + e.printStackTrace(); + } + log("< run MouseWheelThread "); + } + } + + final private class RobotSecurityManager extends SecurityManager{ + // The applet's original security manager. + // There is a bug in some people's Safaris that causes Safari to + // basically hang on liveconnect calls. + // Our security manager fixes it. + + private boolean isActive = false; + private boolean isOpera = false; + private SecurityManager oldsecurity = null; + + public RobotSecurityManager(boolean isOpera, SecurityManager oldsecurity){ + this.isOpera = isOpera; + this.oldsecurity = oldsecurity; + } + + public boolean checkTopLevelWindow(Object window){ + // If our users temporarily accept our cert for a session, + // then use the same session to browse to a malicious website also using our applet, + // that website can automatically execute the applet. + // To resolve this issue, RobotSecurityManager overrides checkTopLevelWindow + // to check the JVM to see if there are other instances of the applet running on different domains. + // If there are, it prompts the user to confirm that they want to run the applet before continuing. + + // null is not supposed to be allowed + // so we allow it to distinguish our security manager. + if(window == null){ + isActive = !isActive; + log("Active is now " + isActive); + } + return window == null ? true : oldsecurity + .checkTopLevelWindow(window); + } + + public void checkPermission(Permission p){ + // liveconnect SocketPermission resolve takes + // FOREVER (like 6 seconds) in Safari + // Java does like 50 of these on the first JS call + // 6*50=300 seconds! + // Opera freaks out though if we deny resolve + if(isActive && !isOpera + && java.net.SocketPermission.class.isInstance(p) + && p.getActions().matches(".*resolve.*")){ + throw new SecurityException( + "DOH: liveconnect resolve locks up Safari. Denying resolve request."); + }else{ + oldsecurity.checkPermission(p); + } + } + + public void checkPermission(Permission perm, Object context){ + checkPermission(perm); + } + } + +} Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/robot.css =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/robot.css (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot/robot.css (revision 0) @@ -0,0 +1,4 @@ +/* robot */ +HTML.dohRobot * { + cursor: progress !important; +} Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/Robot.html =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/Robot.html (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/Robot.html (revision 0) @@ -0,0 +1,141 @@ + + + + + + + + + Index: /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot.js =================================================================== --- /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot.js (revision 0) +++ /Users/justin/Documents/Aptana Studio/fluid-components/src/webapp/tests/fluid-tests/automated/doh/robot.js (revision 0) @@ -0,0 +1,484 @@ +if(window["dojo"]){ + dojo.provide("doh.robot"); + dojo.experimental("doh.robot"); + dojo.require("doh.runner"); +}else if(!doh["robot"]){ + doh.robot={}; +} + +if(!doh.robot["_robotLoaded"]){ +(function(){ + + // loading state + var _robot = null; + + var isSecure = (function(){ + var key = Math.random(); + return function(fcn){ + return key; + }; + })(); + + // no dojo available + // hijack doh.run instead + var _run = doh.run; + doh.run = function(){ + if(!doh.robot._runsemaphore.unlock()){ + // hijack doh._onEnd to clear the applet + // have to do it here because _browserRunner sets it in onload in standalone case + var __onEnd = doh._onEnd; + doh._onEnd = function(){ + doh.robot.killRobot(); + doh._onEnd = __onEnd; + doh._onEnd(); + }; + // if the iframe requested the applet and got a 404, then _robot is obviously unavailable + // at least run the non-robot tests! + if(doh.robot._appletDead){ + doh.robot._onKeyboard(); + }else{ + _robot._callLoaded(isSecure()); + } + } + }; + + var _keyPress = function(/*Number*/ charCode, /*Number*/ keyCode, /*Boolean*/ alt, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Integer, optional*/ delay, /*Boolean*/ async){ + // internal function to type one non-modifier key + + // typecasting Numbers helps Sun's IE plugin lookup methods that take int arguments + + // otherwise JS will send a double and Sun will complain + _robot.typeKey(isSecure(), Number(charCode), Number(keyCode), Boolean(alt), Boolean(ctrl), Boolean(shift), Number(delay||0), Boolean(async||false)); + }; + + doh.robot = { + _robotLoaded: true, + + _killApplet: function(){}, // overridden by Robot.html + + killRobot: function(){ + doh.robot._robotLoaded = false; + document.documentElement.className = document.documentElement.className.replace(/ ?dohRobot/); + doh.robot._killApplet(); + }, + + // Robot init methods + + // controls access to doh.run + // basically, doh.run takes two calls to start the robot: + // one (or more after the robot loads) from the test page + // one from either the applet or an error condition + _runsemaphore: { + lock:["lock"], + unlock:function(){ + try{ + return this.lock.shift(); + }catch(e){ + return null; + } + } + }, + + _initRobot: function(r){ + // called from Robot + // Robot calls _initRobot in its startup sequence + // add dohRobot class to HTML element so tests can use that in CSS rules if desired + document.documentElement.className = document.documentElement.className.replace(/\S$/, "& ") + "dohRobot"; + window.scrollTo(0, 0); +// document.documentElement.scrollTop = document.documentElement.scrollLeft = 0; + _robot = r; + _robot._setKey(isSecure()); + // lazy load + doh.run(); + }, + + // some utility functions to help the iframe use private variables + _run: function(frame){ + frame.style.visibility = "hidden"; + doh.run = _run; + doh.run(); + }, + + _initKeyboard: function(){ + _robot._initKeyboard(isSecure()); + }, + + _initWheel: function(){ + _robot._initWheel(isSecure()); + }, + + _setDocumentBounds: function(docScreenX, docScreenY){ + var robotView = document.getElementById("dohrobotview"); + _robot.setDocumentBounds(isSecure(), Number(docScreenX), Number(docScreenY), Number(robotView.offsetLeft), Number(robotView.offsetTop)); + }, + + _notified: function(keystring){ + _robot._notified(isSecure(), keystring); + }, + + _time:0, + + // if the applet is 404 or cert is denied, this becomes true and kills tests + _appletDead:false, + + _assertRobot:function(){ + // make sure the applet is there and cert accepted + // otherwise, skip the test requesting the robot action + if(doh.robot._appletDead){ throw new Error('doh.robot not available; skipping test.'); } + }, + + _mouseMove: function(/*Number*/ x, /*Number*/ y, /*Boolean*/ absolute, /*Integer, optional*/ duration){ + if(absolute){ + var scroll = {y: (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0), + x: (window.pageXOffset || (window["dojo"]?dojo._fixIeBiDiScrollLeft(document.documentElement.scrollLeft):undefined) || document.body.scrollLeft || 0)}; + y -= scroll.y; + x -= scroll.x; + } + _robot.moveMouse(isSecure(), Number(x), Number(y), Number(0), Number(duration||100)); + }, + + // Main doh.robot API + sequence:function(/*Function*/ f, /*Integer, optional*/ delay, /*Integer, optional*/ duration){ + // summary: + // Defer an action by adding it to the robot's incrementally delayed queue of actions to execute. + // + // f: + // A function containing actions you want to defer. + // + // delay: + // Delay, in milliseconds, to wait before firing. + // The delay is a delta with respect to the previous automation call. + // For example, the following code ends after 600ms: + // doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms + // doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all + // + // duration: + // Delay to wait after firing. + // + + delay = delay || 1; + doh.robot._time += delay; + setTimeout(function(){ + doh.robot._time -= delay; + f(); + if(duration){ + setTimeout(function(){ + doh.robot._time -= duration; + }, duration); + } + }, doh.robot._time); + if(duration){ + doh.robot._time += duration; + } + }, + + typeKeys: function(/*String||Number*/ chars, /*Integer, optional*/ delay, /*Integer, optional*/ duration){ + // summary: + // Types a string of characters in order, or types a dojo.keys.* constant. + // + // description: + // Types a string of characters in order, or types a dojo.keys.* constant. + // Example: doh.robot.typeKeys("dijit.ed", 500); + // + // chars: + // String of characters to type, or a dojo.keys.* constant + // + // delay: + // Delay, in milliseconds, to wait before firing. + // The delay is a delta with respect to the previous automation call. + // For example, the following code ends after 600ms: + // doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms + // doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all + // + // duration: + // Time, in milliseconds, to spend pressing all of the keys. + // + + this._assertRobot(); + this.sequence(function(){ + duration=duration||0; + if(typeof(chars) == Number){ + _keyPress(chars, chars, false, false, false, delay); + }else if(chars.length){ + for(var i = 0; i