source: Dev/branches/rest-dojo-ui/client/util/doh/robot.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 17.8 KB
Line 
1define(["doh/_browserRunner", "require"], function(doh, require){
2
3        // loading state
4        var _robot = null;
5
6        var isSecure = (function(){
7                var key = Math.random();
8                return function(fcn){
9                        return key;
10                };
11        })();
12
13        // no dojo available
14        // hijack doh.run instead
15        var _run = doh.run;
16        doh.run = function(){
17                if(!doh.robot._runsemaphore.unlock()){
18                        // hijack doh._onEnd to clear the applet
19                        // have to do it here because browserRunner sets it in onload in standalone case
20                        var __onEnd = doh._onEnd;
21                        doh._onEnd = function(){
22                                doh.robot.killRobot();
23                                doh._onEnd = __onEnd;
24                                doh._onEnd();
25                        };
26                        doh.robot.startRobot();
27                }
28        };
29
30        var cleanup=function(){
31                doh.robot.killRobot();
32        }
33        if(typeof dojo !== 'undefined'){
34                dojo.addOnUnload(cleanup)
35        }else{
36                window.onunload=cleanup;
37        }
38        var _keyPress = function(/*Number*/ charCode, /*Number*/ keyCode, /*Boolean*/ alt, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Boolean*/ meta, /*Integer, optional*/ delay, /*Boolean*/ async){
39                // internal function to type one non-modifier key
40
41                // typecasting Numbers helps Sun's IE plugin lookup methods that take int arguments
42
43                // otherwise JS will send a double and Sun will complain
44                _robot.typeKey(isSecure(), Number(charCode), Number(keyCode), Boolean(alt), Boolean(ctrl), Boolean(shift), Boolean(meta), Number(delay||0), Boolean(async||false));
45        };
46
47        doh.robot = {
48        _robotLoaded: true,
49        _robotInitialized: false,
50        // prime the event pump for fast browsers like Google Chrome - it's so fast, it doesn't stop to listen for keypresses!
51        _spaceReceived: false,
52        _primePump: false,
53
54        _killApplet: function(){}, // overridden by Robot.html
55
56        killRobot: function(){
57                if(doh.robot._robotLoaded){
58                        doh.robot._robotLoaded = false;
59                        document.documentElement.className = document.documentElement.className.replace(/ ?dohRobot/, "");
60                        doh.robot._killApplet();
61                }
62        },
63
64        // Robot init methods
65
66        // controls access to doh.run
67        // basically, doh.run takes two calls to start the robot:
68        // one (or more after the robot loads) from the test page
69        // one from either the applet or an error condition
70        _runsemaphore: {
71                lock:["lock"],
72                unlock:function(){
73                        try{
74                                return this.lock.shift();
75                        }catch(e){
76                                return null;
77                        }
78                }
79        },
80
81        startRobot: function(){
82                //startRobot should be called to initialize the robot (after the java applet is loaded).
83                //one good place to do this is in a dojo.addOnLoad handler. This function will be called
84                //automatically if it is not already called when doh.run() is invoked.
85                if(!this._robotInitialized){
86                        this._robotInitialized = true;
87                        // if the iframe requested the applet and got a 404, then _robot is obviously unavailable
88                        // at least run the non-robot tests!
89                        if(doh.robot._appletDead){
90                                doh.robot._onKeyboard();
91                        }else{
92                                _robot._callLoaded(isSecure());
93                        }
94                }
95        },
96        _initRobot: function(r){
97                // called from Robot
98                // Robot calls _initRobot in its startup sequence
99
100                // Prevent rerunning the whole test (see #8958 for details)
101                if(doh._initRobotCalled){ return; }
102                doh._initRobotCalled = true;
103
104                // add dohRobot class to HTML element so tests can use that in CSS rules if desired
105                document.documentElement.className += " dohRobot";
106                window.scrollTo(0, 0);
107//              document.documentElement.scrollTop = document.documentElement.scrollLeft = 0;
108                _robot = r;
109                _robot._setKey(isSecure());
110                // lazy load
111                doh.run();
112        },
113
114        // some utility functions to help the iframe use private variables
115        _run: function(frame){
116                frame.style.visibility = "hidden";
117                doh.run = _run;
118                doh.run();
119        },
120
121        _initKeyboard: function(){
122                _robot._initKeyboard(isSecure());
123        },
124
125        _initWheel: function(){
126                _robot._initWheel(isSecure());
127        },
128
129        _setDocumentBounds: function(docScreenX, docScreenY){
130                var robotView = document.getElementById("dohrobotview");
131                _robot.setDocumentBounds(isSecure(), Number(docScreenX), Number(docScreenY), Number(robotView.offsetLeft), Number(robotView.offsetTop));
132        },
133
134        _notified: function(keystring){
135                _robot._notified(isSecure(), keystring);
136        },
137
138        _time:0,
139
140        // if the applet is 404 or cert is denied, this becomes true and kills tests
141        _appletDead:false,
142
143        _assertRobot:function(){
144                // make sure the applet is there and cert accepted
145                // otherwise, skip the test requesting the robot action
146                if(doh.robot._appletDead){ throw new Error('doh.robot not available; skipping test.'); }
147        },
148
149        _mouseMove: function(/*Number*/ x, /*Number*/ y, /*Boolean*/ absolute, /*Integer, optional*/ duration){
150                if(absolute){
151                        var scroll = {y: (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0),
152                        x: (window.pageXOffset || (window["dojo"]?dojo._fixIeBiDiScrollLeft(document.documentElement.scrollLeft):undefined) || document.body.scrollLeft || 0)};
153                        y -= scroll.y;
154                        x -= scroll.x;
155                }
156                _robot.moveMouse(isSecure(), Number(x), Number(y), Number(0), Number(duration||100));
157        },
158
159        // Main doh.robot API
160        sequence:function(/*Function*/ f, /*Integer, optional*/ delay, /*Integer, optional*/ duration){
161                // summary:
162                //              Defer an action by adding it to the robot's incrementally delayed queue of actions to execute.
163                //
164                // f:
165                //              A function containing actions you want to defer.
166                //
167                // delay:
168                //              Delay, in milliseconds, to wait before firing.
169                //              The delay is a delta with respect to the previous automation call.
170                //              For example, the following code ends after 600ms:
171                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
172                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
173                //
174                // duration:
175                //              Delay to wait after firing.
176                //
177
178                var currentTime = (new Date()).getTime();
179                if(currentTime > (doh.robot._time || 0)){
180                        doh.robot._time = currentTime;
181                }
182                doh.robot._time += delay || 1;
183                setTimeout(f, doh.robot._time - currentTime);
184                doh.robot._time += duration || 0;
185        },
186
187        typeKeys: function(/*String||Number*/ chars, /*Integer, optional*/ delay, /*Integer, optional*/ duration){
188                // summary:
189                //              Types a string of characters in order, or types a dojo.keys.* constant.
190                //
191                // description:
192                //              Types a string of characters in order, or types a dojo.keys.* constant.
193                //              Example: doh.robot.typeKeys("dijit.ed", 500);
194                //
195                // chars:
196                //              String of characters to type, or a dojo.keys.* constant
197                //
198                // delay:
199                //              Delay, in milliseconds, to wait before firing.
200                //              The delay is a delta with respect to the previous automation call.
201                //              For example, the following code ends after 600ms:
202                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
203                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
204                //
205                // duration:
206                //              Time, in milliseconds, to spend pressing all of the keys.
207                //              The default is (string length)*50 ms.
208                //
209
210                this._assertRobot();
211                this.sequence(function(){
212                        var isNum = typeof(chars) == Number;
213                        duration=duration||(isNum?50:chars.length*50);
214                        if(isNum){
215                                _keyPress(chars, chars, false, false, false, false, 0);
216                        }else if(chars.length){
217                                _keyPress(chars.charCodeAt(0), 0, false, false, false, false, 0);
218                                for(var i = 1; i<chars.length; i++){
219                                        _keyPress(chars.charCodeAt(i), 0, false, false, false, false, Math.max(Math.floor(duration/chars.length), 0));
220                                }
221                        }
222                }, delay, duration);
223        },
224
225        keyPress: function(/*Integer*/ charOrCode, /*Integer, optional*/ delay, /*Object*/ modifiers, /*Boolean*/ asynchronous){
226                // summary:
227                //              Types a key combination, like SHIFT-TAB.
228                //
229                // description:
230                //              Types a key combination, like SHIFT-TAB.
231                //              Example: to press shift-tab immediately, call doh.robot.keyPress(dojo.keys.TAB, 0, {shift:true})
232                //
233                // charOrCode:
234                //              char/JS keyCode/dojo.keys.* constant for the key you want to press
235                //
236                // delay:
237                //              Delay, in milliseconds, to wait before firing.
238                //              The delay is a delta with respect to the previous automation call.
239                //              For example, the following code ends after 600ms:
240                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
241                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
242                //
243                // modifiers:
244                //              JSON object that represents all of the modifier keys being pressed.
245                //              It takes the following Boolean attributes:
246                //                      - shift
247                //                      - alt
248                //                      - ctrl
249                //                      - meta
250                //
251                // asynchronous:
252                //              If true, the delay happens asynchronously and immediately, outside of the browser's JavaScript thread and any previous calls.
253                //              This is useful for interacting with the browser's modal dialogs.
254                //
255
256                this._assertRobot();
257                if(!modifiers){
258                        modifiers = {alt:false, ctrl:false, shift:false, meta:false};
259                }else{
260                        // normalize modifiers
261                        var attrs = ["alt", "ctrl", "shift", "meta"];
262                        for(var i = 0; i<attrs.length; i++){
263                                if(!modifiers[attrs[i]]){
264                                        modifiers[attrs[i]] = false;
265                                }
266                        }
267                }
268                var isChar = typeof(charOrCode)=="string";
269                if(asynchronous){
270                        _keyPress(isChar?charOrCode.charCodeAt(0):0, isChar?0:charOrCode, modifiers.alt, modifiers.ctrl, modifiers.shift, modifiers.meta, delay, true);
271                        return;
272                }
273                this.sequence(function(){
274                        _keyPress(isChar?charOrCode.charCodeAt(0):0, isChar?0:charOrCode, modifiers.alt, modifiers.ctrl, modifiers.shift, modifiers.meta, 0);
275                },delay);
276        },
277
278        keyDown: function(/*Integer*/ charOrCode, /*Integer, optional*/ delay){
279                // summary:
280                //              Holds down a single key, like SHIFT or 'a'.
281                //
282                // description:
283                //              Holds down a single key, like SHIFT or 'a'.
284                //              Example: to hold down the 'a' key immediately, call doh.robot.keyDown('a')
285                //
286                // charOrCode:
287                //              char/JS keyCode/dojo.keys.* constant for the key you want to hold down
288                //              Warning: holding down a shifted key, like 'A', can have unpredictable results.
289                //
290                // delay:
291                //              Delay, in milliseconds, to wait before firing.
292                //              The delay is a delta with respect to the previous automation call.
293                //              For example, the following code ends after 600ms:
294                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
295                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
296                //
297
298                this._assertRobot();
299                this.sequence(function(){
300                        var isChar = typeof(charOrCode)=="string";
301                        _robot.downKey(isSecure(), isChar?charOrCode:0, isChar?0:charOrCode, 0);
302                },delay);
303        },
304
305        keyUp: function(/*Integer*/ charOrCode, /*Integer, optional*/ delay){
306                // summary:
307                //              Releases a single key, like SHIFT or 'a'.
308                //
309                // description:
310                //              Releases a single key, like SHIFT or 'a'.
311                //              Example: to release the 'a' key immediately, call doh.robot.keyUp('a')
312                //
313                // charOrCode:
314                //              char/JS keyCode/dojo.keys.* constant for the key you want to release
315                //              Warning: releasing a shifted key, like 'A', can have unpredictable results.
316                //
317                // delay:
318                //              Delay, in milliseconds, to wait before firing.
319                //              The delay is a delta with respect to the previous automation call.
320                //              For example, the following code ends after 600ms:
321                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
322                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
323                //
324
325                this._assertRobot();
326                this.sequence(function(){
327                        var isChar=typeof(charOrCode)=="string";
328                        _robot.upKey(isSecure(), isChar?charOrCode:0, isChar?0:charOrCode, 0);
329                },delay);
330        },
331
332
333        mouseClick: function(/*Object*/ buttons, /*Integer, optional*/ delay){
334                // summary:
335                //              Convenience function to do a press/release.
336                //              See doh.robot.mousePress for more info.
337                //
338                // description:
339                //              Convenience function to do a press/release.
340                //              See doh.robot.mousePress for more info.
341                //
342
343                this._assertRobot();
344                doh.robot.mousePress(buttons, delay);
345                doh.robot.mouseRelease(buttons, 1);
346        },
347
348        mousePress: function(/*Object*/ buttons, /*Integer, optional*/ delay){
349                // summary:
350                //              Presses mouse buttons.
351                // description:
352                //              Presses the mouse buttons you pass as true.
353                //              Example: to press the left mouse button, pass {left:true}.
354                //              Mouse buttons you don't specify keep their previous pressed state.
355                //
356                // buttons:     JSON object that represents all of the mouse buttons being pressed.
357                //              It takes the following Boolean attributes:
358                //                      - left
359                //                      - middle
360                //                      - right
361                //
362                // delay:
363                //              Delay, in milliseconds, to wait before firing.
364                //              The delay is a delta with respect to the previous automation call.
365                //              For example, the following code ends after 600ms:
366                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
367                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
368                //
369
370                this._assertRobot();
371                if(!buttons){ return; }
372                this.sequence(function(){
373                        var attrs = ["left", "middle", "right"];
374                        for(var i = 0; i<attrs.length; i++){
375                                if(!buttons[attrs[i]]){
376                                        buttons[attrs[i]] = false;
377                                }
378                        }
379                        _robot.pressMouse(isSecure(), Boolean(buttons.left), Boolean(buttons.middle), Boolean(buttons.right), Number(0));
380                },delay);
381        },
382
383        mouseMove: function(/*Number*/ x, /*Number*/ y, /*Integer, optional*/ delay, /*Integer, optional*/ duration, /*Boolean*/ absolute){
384                // summary:
385                //              Moves the mouse to the specified x,y offset relative to the viewport.
386                //
387                // x:
388                //              x offset relative to the viewport, in pixels, to move the mouse.
389                //
390                // y:
391                //              y offset relative to the viewport, in pixels, to move the mouse.
392                //
393                // delay:
394                //              Delay, in milliseconds, to wait before firing.
395                //              The delay is a delta with respect to the previous automation call.
396                //              For example, the following code ends after 600ms:
397                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
398                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
399                //
400                // duration:
401                //              Approximate time Robot will spend moving the mouse
402                //              The default is 100ms. This also affects how many mousemove events will
403                //              be generated, which is the log of the duration.
404                //
405                // absolute:
406                //              Boolean indicating whether the x and y values are absolute coordinates.
407                //              If false, then mouseMove expects that the x,y will be relative to the window. (clientX/Y)
408                //              If true, then mouseMove expects that the x,y will be absolute. (pageX/Y)
409                //
410
411                this._assertRobot();
412                duration = duration||100;
413                this.sequence(function(){
414                        doh.robot._mouseMove(x, y, absolute, duration);
415                },delay,duration);
416        },
417
418        mouseRelease: function(/*Object*/ buttons, /*Integer, optional*/ delay){
419                // summary:
420                //              Releases mouse buttons.
421                //
422                // description:
423                //              Releases the mouse buttons you pass as true.
424                //              Example: to release the left mouse button, pass {left:true}.
425                //              Mouse buttons you don't specify keep their previous pressed state.
426                //              See doh.robot.mousePress for more info.
427                //
428
429                this._assertRobot();
430                if(!buttons){ return; }
431                this.sequence(function(){
432                        var attrs = ["left", "middle", "right"];
433                        for(var i = 0; i<attrs.length; i++){
434                                if(!buttons[attrs[i]]){
435                                        buttons[attrs[i]] = false;
436                                }
437                        }
438                        _robot.releaseMouse(isSecure(), Boolean(buttons.left), Boolean(buttons.middle), Boolean(buttons.right), Number(0));
439                },delay);
440        },
441
442        // mouseWheelSize: Integer value that determines the amount of wheel motion per unit
443        mouseWheelSize: 1,
444
445        mouseWheel: function(/*Number*/ wheelAmt, /*Integer, optional*/ delay, /*Integer, optional*/ duration){
446                // summary:
447                //              Spins the mouse wheel.
448                //
449                // description:
450                //              Spins the wheel wheelAmt "notches."
451                //              Negative wheelAmt scrolls up/away from the user.
452                //              Positive wheelAmt scrolls down/toward the user.
453                //              Note: this will all happen in one event.
454                //              Warning: the size of one mouse wheel notch is an OS setting.
455                //              You can accesss this size from doh.robot.mouseWheelSize
456                //
457                // wheelAmt:
458                //              Number of notches to spin the wheel.
459                //              Negative wheelAmt scrolls up/away from the user.
460                //              Positive wheelAmt scrolls down/toward the user.
461                //
462                // delay:
463                //              Delay, in milliseconds, to wait before firing.
464                //              The delay is a delta with respect to the previous automation call.
465                //              For example, the following code ends after 600ms:
466                //                      doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
467                //                      doh.robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
468                //
469                // duration:
470                //              Approximate time Robot will spend moving the mouse
471                //              By default, the Robot will wheel the mouse as fast as possible.
472                //
473
474
475                this._assertRobot();
476                if(!wheelAmt){ return; }
477                this.sequence(function(){
478                        _robot.wheelMouse(isSecure(), Number(wheelAmt), Number(0), Number(duration||0));
479                },delay,duration);
480        },
481
482        setClipboard: function(/*String*/data,/*String, optional*/format){
483                // summary:
484                //              Set clipboard content.
485                //
486                // description:
487                //              Set data as clipboard content, overriding anything already there. The
488                //              data will be put to the clipboard using the given format.
489                //
490                // data:
491                //              New clipboard content to set
492                //
493                // format:
494                //              Set this to "text/html" to put richtext to the clipboard.
495                //              Otherwise, data is treated as plaintext. By default, plaintext
496                //              is used.
497                if(format==='text/html'){
498                        _robot.setClipboardHtml(isSecure(),data);
499                }else{
500                        _robot.setClipboardText(isSecure(),data);
501                }
502        }
503        };
504
505        // the applet itself
506        // needs to be down here so the handlers are set up
507        var iframesrc;
508        var scripts = document.getElementsByTagName("script");
509        for(var x = 0; x<scripts.length; x++){
510                var s = scripts[x].getAttribute('src');
511                if(s && (s.substr(s.length-9) == "runner.js")){
512                        iframesrc = s.substr(0, s.length-9)+'Robot.html';
513                        break;
514                }
515        }
516        // if loaded with dojo, there might not be a runner.js!
517        if(!iframesrc && window["dojo"]){
518                // if user set document.domain to something else, send it to the Robot too
519                iframesrc = require.toUrl("./Robot.html") + "?domain=" + escape(document.domain);
520        }
521        document.writeln('<div id="dohrobotview" style="border:0px none; margin:0px; padding:0px; position:absolute; bottom:0px; right:0px; width:1px; height:1px; overflow:hidden; visibility:hidden; background-color:red;"></div>'+
522                '<iframe application="true" style="border:0px none; z-index:32767; padding:0px; margin:0px; position:absolute; left:0px; top:0px; height:42px; width:200px; overflow:hidden; background-color:transparent;" tabIndex="-1" src="'+iframesrc+'" ALLOWTRANSPARENCY="true"></iframe>');
523});
Note: See TracBrowser for help on using the repository browser.