control-freak-ide/server/nodejs/util/doh/plugins/android-webdriver-robot.js
plastic-hub-dev-node-saturn 538369cff7 latest
2021-05-12 18:35:18 +02:00

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;
});