You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
11 KiB
338 lines
11 KiB
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; |
|
});
|
|
|