capybara-webkit/src/capybara.js

351 lines
9.1 KiB
JavaScript
Raw Normal View History

Capybara = {
nextIndex: 0,
nodes: {},
2012-11-18 07:48:24 +00:00
attachedFiles: [],
invoke: function () {
return this[CapybaraInvocation.functionName].apply(this, CapybaraInvocation.arguments);
},
find: function (xpath) {
2011-02-26 21:46:59 +00:00
return this.findRelativeTo(document, xpath);
},
2012-07-12 03:36:22 +00:00
currentUrl: function () {
return window.location.toString();
},
2011-02-26 21:46:59 +00:00
findWithin: function (index, xpath) {
return this.findRelativeTo(this.nodes[index], xpath);
},
findRelativeTo: function (reference, xpath) {
var iterator = document.evaluate(xpath, reference, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
var node;
var results = [];
while (node = iterator.iterateNext()) {
this.nextIndex++;
this.nodes[this.nextIndex] = node;
results.push(this.nextIndex);
}
return results.join(",");
},
2011-09-30 21:13:54 +00:00
isAttached: function(index) {
return document.evaluate("ancestor-or-self::html", this.nodes[index], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
},
text: function (index) {
2011-07-22 19:21:17 +00:00
var node = this.nodes[index];
var type = (node.type || node.tagName).toLowerCase();
if (type == "textarea") {
return node.innerHTML;
} else {
return node.innerText || node.textContent;
2011-07-22 19:21:17 +00:00
}
},
attribute: function (index, name) {
switch(name) {
2011-07-22 19:21:17 +00:00
case 'checked':
return this.nodes[index].checked;
break;
2011-07-22 19:21:17 +00:00
case 'disabled':
return this.nodes[index].disabled;
break;
case 'multiple':
return this.nodes[index].multiple;
break;
default:
return this.nodes[index].getAttribute(name);
}
2011-02-26 19:55:40 +00:00
},
hasAttribute: function(index, name) {
return this.nodes[index].hasAttribute(name);
},
2011-09-24 22:16:36 +00:00
path: function(index) {
return "/" + this.getXPathNode(this.nodes[index]).join("/");
},
getXPathNode: function(node, path) {
path = path || [];
if (node.parentNode) {
2011-09-24 22:16:36 +00:00
path = this.getXPathNode(node.parentNode, path);
}
var first = node;
while (first.previousSibling)
first = first.previousSibling;
var count = 0;
var index = 0;
var iter = first;
while (iter) {
if (iter.nodeType == 1 && iter.nodeName == node.nodeName)
count++;
if (iter.isSameNode(node))
index = count;
iter = iter.nextSibling;
continue;
2011-09-24 22:16:36 +00:00
}
if (node.nodeType == 1)
path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 1 ? "["+index+"]" : ''));
2011-09-24 22:16:36 +00:00
return path;
},
2011-02-26 19:55:40 +00:00
tagName: function(index) {
2011-02-26 20:04:34 +00:00
return this.nodes[index].tagName.toLowerCase();
2011-02-26 20:18:11 +00:00
},
submit: function(index) {
return this.nodes[index].submit();
},
clickTest: function(node, x, y) {
var el = document.elementFromPoint(x, y);
while (el) {
if (el === node)
return true;
else
el = el.parentNode;
}
return false;
},
2011-02-26 20:18:11 +00:00
click: function (index) {
2012-12-11 03:53:03 +00:00
var node = this.nodes[index];
node.scrollIntoViewIfNeeded();
var rect = node.getClientRects()[0];
return CapybaraInvocation.click(node, rect.left, rect.top, rect.width, rect.height);
2011-02-26 20:50:45 +00:00
},
trigger: function (index, eventName) {
var eventObject = document.createEvent("HTMLEvents");
eventObject.initEvent(eventName, true, true);
this.nodes[index].dispatchEvent(eventObject);
2011-02-26 21:20:05 +00:00
},
keypress: function(index, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
var eventObject = document.createEvent("Events");
eventObject.initEvent('keypress', true, true);
eventObject.window = window;
eventObject.altKey = altKey;
eventObject.ctrlKey = ctrlKey;
eventObject.shiftKey = shiftKey;
eventObject.metaKey = metaKey;
eventObject.keyCode = keyCode;
eventObject.charCode = charCode;
eventObject.which = keyCode;
this.nodes[index].dispatchEvent(eventObject);
},
keyupdown: function(index, eventName, keyCode) {
var eventObject = document.createEvent("HTMLEvents");
eventObject.initEvent(eventName, true, true);
eventObject.keyCode = keyCode;
eventObject.which = keyCode;
eventObject.charCode = 0;
this.nodes[index].dispatchEvent(eventObject);
},
2011-02-26 21:20:05 +00:00
visible: function (index) {
var element = this.nodes[index];
while (element) {
var style = element.ownerDocument.defaultView.getComputedStyle(element, null);
if (style.getPropertyValue("display") == 'none' || style.getPropertyValue("visibility") == 'hidden')
2011-02-26 21:20:05 +00:00
return false;
2011-02-26 21:20:05 +00:00
element = element.parentElement;
}
return true;
2011-02-26 20:48:44 +00:00
},
2011-07-29 15:18:51 +00:00
selected: function (index) {
return this.nodes[index].selected;
},
2011-02-26 20:48:44 +00:00
value: function(index) {
return this.nodes[index].value;
2011-02-26 21:26:22 +00:00
},
getInnerHTML: function(index) {
return this.nodes[index].innerHTML;
},
2012-09-26 21:30:35 +00:00
setInnerHTML: function(index, value) {
this.nodes[index].innerHTML = value;
2012-09-26 21:30:35 +00:00
return true;
},
characterToKeyCode: function(character) {
var code = character.toUpperCase().charCodeAt(0);
var specialKeys = {
96: 192, //`
45: 189, //-
61: 187, //=
91: 219, //[
93: 221, //]
92: 220, //\
59: 186, //;
39: 222, //'
44: 188, //,
46: 190, //.
47: 191, ///
127: 46, //delete
126: 192, //~
33: 49, //!
64: 50, //@
35: 51, //#
36: 52, //$
37: 53, //%
94: 54, //^
38: 55, //&
42: 56, //*
40: 57, //(
41: 48, //)
95: 189, //_
43: 187, //+
123: 219, //{
125: 221, //}
124: 220, //|
58: 186, //:
34: 222, //"
60: 188, //<
62: 190, //>
63: 191 //?
};
if (specialKeys[code]) {
code = specialKeys[code];
}
return code;
},
set: function (index, value) {
var length, maxLength, node, strindex, textTypes, type;
node = this.nodes[index];
type = (node.type || node.tagName).toLowerCase();
textTypes = ["email", "number", "password", "search", "tel", "text", "textarea", "url"];
if (textTypes.indexOf(type) != -1) {
this.focus(index);
maxLength = this.attribute(index, "maxlength");
if (maxLength && value.length > maxLength) {
length = maxLength;
} else {
length = value.length;
}
node.value = "";
for (strindex = 0; strindex < length; strindex++) {
node.value += value[strindex];
var keyCode = this.characterToKeyCode(value[strindex]);
this.keyupdown(index, "keydown", keyCode);
this.keypress(index, false, false, false, false, value.charCodeAt(strindex), value.charCodeAt(strindex));
this.keyupdown(index, "keyup", keyCode);
this.trigger(index, "input");
}
this.trigger(index, "change");
} else if (type === "checkbox" || type === "radio") {
2012-01-27 23:03:38 +00:00
if (node.checked != (value === "true")) {
this.click(index);
2012-01-27 23:03:38 +00:00
}
} else if (type === "file") {
2012-11-18 07:48:24 +00:00
this.attachedFiles = Array.prototype.slice.call(arguments, 1);
this.click(index);
} else {
node.value = value;
}
2011-02-26 22:51:47 +00:00
},
focus: function(index) {
this.nodes[index].focus();
},
2011-02-26 22:51:47 +00:00
selectOption: function(index) {
this.nodes[index].selected = true;
this.trigger(index, "change");
2011-02-26 22:51:47 +00:00
},
unselectOption: function(index) {
this.nodes[index].selected = false;
this.trigger(index, "change");
2011-04-23 11:22:16 +00:00
},
2011-02-26 22:51:47 +00:00
2013-01-30 12:06:59 +00:00
centerPosition: function(element) {
2011-04-23 11:22:16 +00:00
this.reflow(element);
var rect = element.getBoundingClientRect();
var position = {
x: rect.width / 2,
y: rect.height / 2
};
do {
position.x += element.offsetLeft;
position.y += element.offsetTop;
} while ((element = element.offsetParent));
position.x = Math.floor(position.x);
position.y = Math.floor(position.y);
2011-04-23 11:22:16 +00:00
return position;
},
reflow: function(element, force) {
if (force || element.offsetWidth === 0) {
var prop, oldStyle = {}, newStyle = {position: "absolute", visibility : "hidden", display: "block" };
for (prop in newStyle) {
oldStyle[prop] = element.style[prop];
element.style[prop] = newStyle[prop];
}
// force reflow
element.offsetWidth;
element.offsetHeight;
2011-04-23 11:22:16 +00:00
for (prop in oldStyle)
element.style[prop] = oldStyle[prop];
}
},
dragTo: function (index, targetIndex) {
var element = this.nodes[index], target = this.nodes[targetIndex];
2013-01-30 12:06:59 +00:00
var position = this.centerPosition(element);
2011-04-23 11:22:16 +00:00
var options = {
clientX: position.x,
clientY: position.y
};
var mouseTrigger = function(eventName, options) {
var eventObject = document.createEvent("MouseEvents");
eventObject.initMouseEvent(eventName, true, true, window, 0, 0, 0, options.clientX || 0, options.clientY || 0, false, false, false, false, 0, null);
element.dispatchEvent(eventObject);
};
2011-04-23 11:22:16 +00:00
mouseTrigger('mousedown', options);
options.clientX += 1;
options.clientY += 1;
2011-04-23 11:22:16 +00:00
mouseTrigger('mousemove', options);
2013-01-30 12:06:59 +00:00
position = this.centerPosition(target);
options = {
2011-04-23 11:22:16 +00:00
clientX: position.x,
clientY: position.y
};
mouseTrigger('mousemove', options);
mouseTrigger('mouseup', options);
2012-11-18 00:30:29 +00:00
},
equals: function(index, targetIndex) {
return this.nodes[index] === this.nodes[targetIndex];
2011-04-23 11:22:16 +00:00
}
};