339 lines
11 KiB
JavaScript
339 lines
11 KiB
JavaScript
define(["dojo/has", "dojo/_base/xhr", "doh/runner", "dojo/sniff", "dojo/dom-geometry", "doh/plugins/remoteRobot"], function(has, xhr, doh, sniff, geom, remoteRobotURL){
|
|
// summary:
|
|
// Wraps WebDriver APIs around doh.robot API.
|
|
// Should be loaded as a doh plugin.
|
|
// In theory, with a change to the base URL, this same file could also be used for iOS WebDriver.
|
|
// WebDriver must be modified to accept cross-domain requests (currently it sends incomplete headers).
|
|
//
|
|
|
|
var top=window.parent?window.parent:window;
|
|
|
|
// short-term FIFO command queue, similar to the one in the applet
|
|
var commands=[];
|
|
var _inFlight=false;
|
|
// send requests to the WebDriver and get its JSON text back
|
|
function robotXHR(args){
|
|
var commandString=args.commandString;
|
|
var deferred=args.deferred;
|
|
var immediate=args.immediate;
|
|
//console.debug("remote url: "+remoteRobotURL+"/"+commandString);
|
|
if(immediate||!_inFlight){
|
|
_inFlight=true;
|
|
xhr(args.method,{
|
|
url:remoteRobotURL+"/"+commandString,
|
|
headers: { "Content-Type": "application/json"},
|
|
postData:args.postData,
|
|
load:function(response){
|
|
//console.debug("success sending webdriver command: ", response);
|
|
_inFlight=false;
|
|
if(deferred){
|
|
deferred.callback(response);
|
|
}
|
|
if(commands.length){
|
|
robotXHR(commands.shift());
|
|
}
|
|
},
|
|
error:function(response){
|
|
console.error("failure sending webdriver command: ", response);
|
|
}
|
|
});
|
|
}else{
|
|
commands.push(args);
|
|
}
|
|
}
|
|
|
|
// record stats about last mouse position
|
|
var lastX=0;
|
|
var lastY=0;
|
|
var mouse=null; // debug mouse cursor so you can see what is being touched; created on demand
|
|
// map dojo keys to WebDriver key codes
|
|
// enable/disable as we discover which ones are legit
|
|
|
|
var keyMap={};
|
|
if(window["dojo"]){
|
|
// keyMap[dojo.keys.BACKSPACE]=0xE003;
|
|
//keyMap[dojo.keys.TAB]=0xE004;
|
|
// keyMap[dojo.keys.CLEAR]=0xE005;
|
|
// keyMap[dojo.keys.ENTER]=0xE007;
|
|
// keyMap[dojo.keys.SHIFT]=0xE008;
|
|
// keyMap[dojo.keys.CTRL]=0xE009;
|
|
// keyMap[dojo.keys.ALT]=0xE00A;
|
|
// keyMap[dojo.keys.META]=0xE03D; // the apple key on macs
|
|
// keyMap[dojo.keys.PAUSE]=0xE00B;
|
|
// // dojo.keys.CAPS_LOCK: 20,
|
|
// keyMap[dojo.keys.ESCAPE]=0xE00C;
|
|
// keyMap[dojo.keys.SPACE]=0xE00D;
|
|
// keyMap[dojo.keys.PAGE_UP]=0xE00E;
|
|
// keyMap[dojo.keys.PAGE_DOWN]=0xE00F;
|
|
keyMap[dojo.keys.END]=0xE010;
|
|
keyMap[dojo.keys.HOME]=0xE011;
|
|
keyMap[dojo.keys.LEFT_ARROW]=0xE012;
|
|
keyMap[dojo.keys.UP_ARROW]=0xE013;
|
|
keyMap[dojo.keys.RIGHT_ARROW]=0xE014;
|
|
keyMap[dojo.keys.DOWN_ARROW]=0xE015;
|
|
// keyMap[dojo.keys.INSERT]=0xE016;
|
|
// keyMap[dojo.keys.DELETE]=0xE017;
|
|
// // dojo.keys.HELP: 47,
|
|
// // dojo.keys.LEFT_WINDOW: 91,
|
|
// // dojo.keys.RIGHT_WINDOW: 92,
|
|
// // dojo.keys.SELECT: 93,
|
|
// keyMap[dojo.keys.NUMPAD_0]=0xE01A;
|
|
// keyMap[dojo.keys.NUMPAD_1]=0xE01B;
|
|
// keyMap[dojo.keys.NUMPAD_2]=0xE01C;
|
|
// keyMap[dojo.keys.NUMPAD_3]=0xE01D;
|
|
// keyMap[dojo.keys.NUMPAD_4]=0xE01E;
|
|
// keyMap[dojo.keys.NUMPAD_5]=0xE01F;
|
|
// keyMap[dojo.keys.NUMPAD_6]=0xE020;
|
|
// keyMap[dojo.keys.NUMPAD_7]=0xE021;
|
|
// keyMap[dojo.keys.NUMPAD_8]=0xE022;
|
|
// keyMap[dojo.keys.NUMPAD_9]=0xE023;
|
|
// keyMap[dojo.keys.NUMPAD_MULTIPLY]=0xE024;
|
|
// keyMap[dojo.keys.NUMPAD_PLUS]=0xE025;
|
|
// keyMap[dojo.keys.NUMPAD_ENTER]=0xE026;
|
|
// keyMap[dojo.keys.NUMPAD_MINUS]=0xE027;
|
|
// keyMap[dojo.keys.NUMPAD_PERIOD]=0xE028;
|
|
// keyMap[dojo.keys.NUMPAD_DIVIDE]=0xE029;
|
|
keyMap[dojo.keys.F1]=0xE031;
|
|
keyMap[dojo.keys.F2]=0xE032;
|
|
keyMap[dojo.keys.F3]=0xE033;
|
|
keyMap[dojo.keys.F4]=0xE034;
|
|
keyMap[dojo.keys.F5]=0xE035;
|
|
keyMap[dojo.keys.F6]=0xE036;
|
|
keyMap[dojo.keys.F7]=0xE037;
|
|
keyMap[dojo.keys.F8]=0xE038;
|
|
keyMap[dojo.keys.F9]=0xE039;
|
|
keyMap[dojo.keys.F10]=0xE03A;
|
|
keyMap[dojo.keys.F11]=0xE03B;
|
|
keyMap[dojo.keys.F12]=0xE03C;
|
|
}
|
|
var lastElement={node:null,reference:""};
|
|
// replace applet with methods to call WebDriver
|
|
_robot={
|
|
_setKey:function(sec){
|
|
// initialization
|
|
// switch WebDriver to test frame so it can resolve elements!
|
|
/*robotXHR({
|
|
method:"POST",
|
|
commandString:"frame",
|
|
postData:'{"id":"testBody"}',
|
|
deferred:null,
|
|
immediate:false
|
|
});*/
|
|
// skip keyboard initialization
|
|
doh.robot._onKeyboard();
|
|
},
|
|
|
|
_callLoaded:function(sec){
|
|
// shouldn't be called
|
|
},
|
|
|
|
_initKeyboard:function(sec){
|
|
// shouldn't be called
|
|
},
|
|
|
|
_initWheel:function(sec){
|
|
// shouldn't be called
|
|
},
|
|
|
|
setDocumentBounds:function(sec, docScreenX, docScreenY, width, height){
|
|
// shouldn't be called
|
|
},
|
|
|
|
_spaceReceived:function(){
|
|
// shouldn't be called
|
|
},
|
|
|
|
_notified:function(sec, keystring){
|
|
// shouldn't be called
|
|
},
|
|
|
|
_nextKeyGroupACK: function(sec){
|
|
// shouldn't be called
|
|
},
|
|
|
|
typeKey:function(sec, charCode, keyCode, alt, ctrl, shift, meta, delay, async){
|
|
// send keys to active element
|
|
var deferred=new dojo.Deferred();
|
|
// after active element received...
|
|
deferred.then(function(response){
|
|
// remove garbage characters
|
|
response=response.replace(/\{/g,"({").replace(/\}/g,"})");
|
|
response=response.replace(/[^ -~]/g, "");
|
|
//response=response.substring(0,response.lastIndexOf(")")+1);
|
|
var json=dojo.fromJson(response);
|
|
var activeElement=json.value.ELEMENT;
|
|
// send keys to active element
|
|
var keys=(ctrl?'"'+String.fromCharCode(dojo.keys.CTRL)+'",':'')
|
|
+(shift?'"'+String.fromCharCode(dojo.keys.SHIFT)+'",':'');
|
|
if(keyCode in keyMap){
|
|
keys+='"'+String.fromCharCode(keyMap[keyCode])+'"';
|
|
}else if(keyCode){
|
|
keys+='"'+String.fromCharCode(keyCode)+'"';
|
|
}else{
|
|
keys+='"'+String.fromCharCode(charCode)+'"';
|
|
}
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"element/"+activeElement+"/value",
|
|
postData:'{"value":['+keys+']}',
|
|
deferred:null,
|
|
immediate:false
|
|
});
|
|
});
|
|
// get active element to send keys to
|
|
// strangely, this request is a POST in the WebDriver API??
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"execute",
|
|
postData:'{"script":"return document.activeElement;","args":[]}',
|
|
deferred:deferred,
|
|
immediate:false
|
|
});
|
|
},
|
|
|
|
/* ---- do we need to add the keyUp and keyDown here? */
|
|
upKey:function(sec,charCode,keyCode,delay){
|
|
//robotXHR("upKey?sec="+sec+"&charCode="+charCode+"&keyCode="+keyCode+"&delay="+delay);
|
|
},
|
|
|
|
downKey:function(sec,charCode,keyCode,delay){
|
|
//robotXHR("downKey?sec="+sec+"&charCode="+charCode+"&keyCode="+keyCode+"&delay="+delay);
|
|
},
|
|
|
|
moveMouse:function(sec, x, y, delay, duration){
|
|
// x,y are not being computed relative to the mobile screen for some reason (probably iframe)
|
|
x=Math.round(x);
|
|
y=Math.round(y);
|
|
if(!mouse){
|
|
// create fake mouse
|
|
mouse=dojo.doc.createElement("div");
|
|
dojo.style(mouse, {
|
|
// x, y relative to screen (same coordinates as WebDriver)
|
|
position:"fixed",
|
|
left:"0px",
|
|
top:"0px",
|
|
width:"5px",
|
|
height:"5px",
|
|
"background-color":"red"
|
|
});
|
|
dojo.body().appendChild(mouse);
|
|
}
|
|
// fix x and y
|
|
lastX=x-top.scrollX;
|
|
lastY=y-top.scrollY;
|
|
// cursor needs to be away from center of event or else the cursor itself will interfere with the event!
|
|
mouse.style.left=(x+5)+"px";
|
|
mouse.style.top=(y+5)+"px";
|
|
//mouse.style.left=((lastX+top.scrollX+dojo.global.scrollX)+5)+"px";
|
|
//mouse.style.top=((lastY+top.scrollY+dojo.global.scrollY)+5)+"px";
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"touch/move",
|
|
postData:'{"x":'+lastX+',"y":'+lastX+'}',
|
|
//content:{x:x,y:y},
|
|
deferred:null,
|
|
immediate:false
|
|
});
|
|
},
|
|
|
|
pressMouse:function(sec, left, middle, right, delay){
|
|
//robotXHR("pressMouse?sec="+sec+"&left="+left+"&middle="+middle+"&right="+right+"&delay="+delay,null,true);
|
|
var deferred=new dojo.Deferred();
|
|
deferred.then(function(){
|
|
mouse.style.backgroundColor="yellow";
|
|
});
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"touch/down",
|
|
postData:'{"x":'+(lastX)+',"y":'+(lastY)+'}',
|
|
//content:{x:lastX,y:lastY},
|
|
deferred:deferred,
|
|
immediate:false
|
|
});
|
|
},
|
|
|
|
releaseMouse:function(sec, left, middle, right, delay){
|
|
//robotXHR("releaseMouse?sec="+sec+"&left="+left+"&middle="+middle+"&right="+right+"&delay="+delay,null,true);
|
|
var deferred=new dojo.Deferred();
|
|
deferred.then(function(){
|
|
mouse.style.backgroundColor="red";
|
|
});
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"touch/up",
|
|
postData:'{"x":'+(lastX+1)+',"y":'+(lastY+1)+'}',
|
|
//content:{x:lastX,y:lastY},
|
|
deferred:deferred,
|
|
immediate:false
|
|
});
|
|
},
|
|
|
|
// doh.robot will call these functions in place of its own
|
|
typeKeys:function(/*String||Number*/ chars, /*Integer, optional*/ delay, /*Integer, optional*/ duration){
|
|
// send keys to active element
|
|
var deferred=new dojo.Deferred();
|
|
// after active element received...
|
|
deferred.then(function(response){
|
|
response=response.replace(/\{/g,"({").replace(/\}/g,"})");
|
|
response=response.replace(/[^ -~]/g, "");
|
|
//response=response.substring(0,response.lastIndexOf(")")+1);
|
|
var json=dojo.fromJson(response);
|
|
var activeElement=json.value.ELEMENT;
|
|
// send keys to active element
|
|
var deferred2= new dojo.Deferred();
|
|
deferred2.then(function(){
|
|
lastElement={node:dojo.doc.activeElement,reference:activeElement};
|
|
});
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"element/"+activeElement+"/value",
|
|
// TODO: add codes to sent array for press and release modifiers
|
|
postData:'{"value":["'+chars+'"]}',
|
|
deferred:deferred2,
|
|
immediate:false
|
|
});
|
|
});
|
|
// get active element to send keys to
|
|
// strangely, this request is a POST in the WebDriver API??
|
|
if(dojo.doc.activeElement!=lastElement.node){
|
|
lastElement={node:dojo.doc.activeElement,reference:null};
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"execute",
|
|
postData:"{script:'return document.activeElement;',args:[]}",
|
|
deferred:deferred,
|
|
immediate:false
|
|
});
|
|
}else{
|
|
deferred.callback("{value:{ELEMENT:'"+lastElement.reference+"'}}");
|
|
}
|
|
},
|
|
|
|
_scrollIntoView:function(node){
|
|
// for whatever reason, scrollIntoView does not work...
|
|
//node.scrollIntoView(true);
|
|
p = geom.position(node);
|
|
// scrolling the iframe doesn't seem to do anything
|
|
//dojo.global.scrollTo(p.x,p.y);
|
|
// this seems to work
|
|
top.scrollTo(p.x,p.y);
|
|
// this is also reasonable
|
|
/*var scrollBy={dx:p.x-top.scrollX,dy:p.y-top.scrollY};
|
|
robotXHR({
|
|
method:"POST",
|
|
commandString:"touch/scroll",
|
|
// TODO: add codes to sent array for press and release modifiers
|
|
postData:'{"xoffset":'+scrollBy.dx+', "yoffset":'+scrollBy.dy+'}',
|
|
deferred:null,
|
|
immediate:false
|
|
});*/
|
|
}
|
|
};
|
|
|
|
// robot.js will use this robot instead of the applet
|
|
has.add("doh-custom-robot", function(){
|
|
return has("android") && _robot;
|
|
});
|
|
return _robot;
|
|
});
|