2011-02-26 03:32:22 +00:00
|
|
|
Capybara = {
|
|
|
|
nextIndex: 0,
|
|
|
|
nodes: {},
|
2012-11-18 07:48:24 +00:00
|
|
|
attachedFiles: [],
|
2017-02-07 01:29:11 +00:00
|
|
|
keyModifiersStack: [],
|
2011-02-26 03:32:22 +00:00
|
|
|
|
2011-02-26 03:57:55 +00:00
|
|
|
invoke: function () {
|
2013-01-20 23:57:04 +00:00
|
|
|
try {
|
2017-01-04 00:33:42 +00:00
|
|
|
if (CapybaraInvocation.functionName == "leftClick") {
|
2018-01-06 20:26:31 +00:00
|
|
|
var args = CapybaraInvocation.arguments;
|
|
|
|
var leftClickOptions = this["verifiedClickPosition"].apply(this, args);
|
|
|
|
leftClickOptions["keys"] = JSON.parse(args[1]);
|
|
|
|
offset = JSON.parse(args[2]);
|
|
|
|
if (offset && offset.x && offset.y){
|
|
|
|
leftClickOptions["absoluteX"] = leftClickOptions["absoluteLeft"] + offset.x;
|
|
|
|
leftClickOptions["absoluteY"] = leftClickOptions["absoluteTop"] + offset.y;
|
|
|
|
}
|
|
|
|
return leftClickOptions;
|
2017-01-04 00:33:42 +00:00
|
|
|
} else {
|
|
|
|
return this[CapybaraInvocation.functionName].apply(this, CapybaraInvocation.arguments);
|
|
|
|
}
|
2013-01-20 23:57:04 +00:00
|
|
|
} catch (e) {
|
|
|
|
CapybaraInvocation.error = e;
|
|
|
|
}
|
2011-02-26 03:57:55 +00:00
|
|
|
},
|
|
|
|
|
2013-02-26 19:44:22 +00:00
|
|
|
findXpath: function (xpath) {
|
|
|
|
return this.findXpathRelativeTo(document, xpath);
|
2011-02-26 21:46:59 +00:00
|
|
|
},
|
|
|
|
|
2013-02-25 17:04:12 +00:00
|
|
|
findCss: function (selector) {
|
2013-02-26 19:44:22 +00:00
|
|
|
return this.findCssRelativeTo(document, selector);
|
2013-02-25 17:04:12 +00:00
|
|
|
},
|
|
|
|
|
2013-02-26 19:44:22 +00:00
|
|
|
findXpathWithin: function (index, xpath) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.findXpathRelativeTo(this.getNode(index), xpath);
|
2011-02-26 21:46:59 +00:00
|
|
|
},
|
|
|
|
|
2013-02-25 17:04:12 +00:00
|
|
|
findCssWithin: function (index, selector) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.findCssRelativeTo(this.getNode(index), selector);
|
2013-02-25 17:04:12 +00:00
|
|
|
},
|
|
|
|
|
2013-02-26 19:44:22 +00:00
|
|
|
findXpathRelativeTo: function (reference, xpath) {
|
2011-02-26 21:46:59 +00:00
|
|
|
var iterator = document.evaluate(xpath, reference, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
|
2011-02-26 03:32:22 +00:00
|
|
|
var node;
|
|
|
|
var results = [];
|
|
|
|
while (node = iterator.iterateNext()) {
|
2017-01-27 21:48:04 +00:00
|
|
|
results.push(this.registerNode(node));
|
2011-02-26 03:32:22 +00:00
|
|
|
}
|
2011-02-26 03:33:47 +00:00
|
|
|
return results.join(",");
|
2011-02-26 03:32:22 +00:00
|
|
|
},
|
|
|
|
|
2013-02-26 19:44:22 +00:00
|
|
|
findCssRelativeTo: function (reference, selector) {
|
2013-02-25 17:04:12 +00:00
|
|
|
var elements = reference.querySelectorAll(selector);
|
|
|
|
var results = [];
|
|
|
|
for (var i = 0; i < elements.length; i++) {
|
2017-01-27 21:48:04 +00:00
|
|
|
results.push(this.registerNode(elements[i]));
|
2013-02-25 17:04:12 +00:00
|
|
|
}
|
|
|
|
return results.join(",");
|
|
|
|
},
|
|
|
|
|
2011-09-30 21:13:54 +00:00
|
|
|
isAttached: function(index) {
|
2013-04-02 23:25:26 +00:00
|
|
|
return this.nodes[index] &&
|
|
|
|
document.evaluate("ancestor-or-self::html", this.nodes[index], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
|
2011-09-30 21:13:54 +00:00
|
|
|
},
|
|
|
|
|
2013-12-06 15:43:17 +00:00
|
|
|
getNode: function(index) {
|
|
|
|
if (CapybaraInvocation.allowUnattached || this.isAttached(index)) {
|
|
|
|
return this.nodes[index];
|
|
|
|
} else {
|
|
|
|
throw new Capybara.NodeNotAttachedError(index);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-02-26 04:39:29 +00:00
|
|
|
text: function (index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
var node = this.getNode(index);
|
2017-06-02 18:35:08 +00:00
|
|
|
var type = node instanceof HTMLFormElement ? 'form' : (node.type || node.tagName).toLowerCase();
|
|
|
|
|
2014-06-18 22:00:34 +00:00
|
|
|
if (!this.isNodeVisible(node)) {
|
|
|
|
return '';
|
|
|
|
} else if (type == "textarea") {
|
2011-07-22 19:21:17 +00:00
|
|
|
return node.innerHTML;
|
|
|
|
} else {
|
2015-01-20 23:50:32 +00:00
|
|
|
visible_text = node.innerText;
|
|
|
|
return typeof visible_text === "string" ? visible_text : node.textContent;
|
2011-07-22 19:21:17 +00:00
|
|
|
}
|
2011-02-26 04:39:29 +00:00
|
|
|
},
|
|
|
|
|
2013-02-18 04:08:53 +00:00
|
|
|
allText: function (index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
var node = this.getNode(index);
|
2013-02-18 04:08:53 +00:00
|
|
|
return node.textContent;
|
|
|
|
},
|
|
|
|
|
2011-02-26 03:32:22 +00:00
|
|
|
attribute: function (index, name) {
|
2013-12-06 15:43:17 +00:00
|
|
|
var node = this.getNode(index);
|
2017-11-08 17:11:56 +00:00
|
|
|
if (node.hasAttribute(name)) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return node.getAttribute(name);
|
2011-03-11 16:25:06 +00:00
|
|
|
}
|
2017-11-08 17:11:56 +00:00
|
|
|
return void 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
property: function (index, name) {
|
|
|
|
var node = this.getNode(index);
|
|
|
|
return node[name];
|
2011-02-26 19:55:40 +00:00
|
|
|
},
|
|
|
|
|
2012-11-21 19:40:36 +00:00
|
|
|
hasAttribute: function(index, name) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.getNode(index).hasAttribute(name);
|
2012-11-21 19:40:36 +00:00
|
|
|
},
|
|
|
|
|
2011-09-24 22:16:36 +00:00
|
|
|
path: function(index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.pathForNode(this.getNode(index));
|
2013-06-05 19:34:15 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
pathForNode: function(node) {
|
|
|
|
return "/" + this.getXPathNode(node).join("/");
|
2011-09-24 22:16:36 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getXPathNode: function(node, path) {
|
|
|
|
path = path || [];
|
2011-10-22 23:25:07 +00:00
|
|
|
if (node.parentNode) {
|
2011-09-24 22:16:36 +00:00
|
|
|
path = this.getXPathNode(node.parentNode, path);
|
|
|
|
}
|
|
|
|
|
2011-10-22 23:25:07 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2011-10-22 23:25:07 +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) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.getNode(index).tagName.toLowerCase();
|
2011-02-26 20:18:11 +00:00
|
|
|
},
|
|
|
|
|
2011-09-24 22:16:57 +00:00
|
|
|
submit: function(index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.getNode(index).submit();
|
2011-09-24 22:16:57 +00:00
|
|
|
},
|
|
|
|
|
2013-06-05 19:34:15 +00:00
|
|
|
expectNodeAtPosition: function(node, pos) {
|
|
|
|
var nodeAtPosition =
|
|
|
|
document.elementFromPoint(pos.relativeX, pos.relativeY);
|
|
|
|
var overlappingPath;
|
|
|
|
|
|
|
|
if (nodeAtPosition)
|
|
|
|
overlappingPath = this.pathForNode(nodeAtPosition)
|
|
|
|
|
|
|
|
if (!this.isNodeOrChildAtPosition(node, pos, nodeAtPosition))
|
|
|
|
throw new Capybara.ClickFailed(
|
|
|
|
this.pathForNode(node),
|
|
|
|
overlappingPath,
|
|
|
|
pos
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
isNodeOrChildAtPosition: function(expectedNode, pos, currentNode) {
|
|
|
|
if (currentNode == expectedNode) {
|
|
|
|
return CapybaraInvocation.clickTest(
|
|
|
|
expectedNode,
|
|
|
|
pos.absoluteX,
|
|
|
|
pos.absoluteY
|
|
|
|
);
|
|
|
|
} else if (currentNode) {
|
|
|
|
return this.isNodeOrChildAtPosition(
|
|
|
|
expectedNode,
|
|
|
|
pos,
|
|
|
|
currentNode.parentNode
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return false;
|
2013-01-12 23:02:10 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-01-21 00:13:58 +00:00
|
|
|
clickPosition: function(node) {
|
2016-03-25 17:04:43 +00:00
|
|
|
if(node.namespaceURI == 'http://www.w3.org/2000/svg') {
|
|
|
|
var rect = node.getBoundingClientRect();
|
2013-03-04 00:57:42 +00:00
|
|
|
|
|
|
|
if (rect.width > 0 && rect.height > 0)
|
|
|
|
return CapybaraInvocation.clickPosition(node, rect.left, rect.top, rect.width, rect.height);
|
2016-03-25 17:04:43 +00:00
|
|
|
} else {
|
|
|
|
var rects = node.getClientRects();
|
|
|
|
var rect;
|
|
|
|
|
|
|
|
for (var i = 0; i < rects.length; i++) {
|
|
|
|
rect = rects[i];
|
|
|
|
if (rect.width > 0 && rect.height > 0)
|
|
|
|
return CapybaraInvocation.clickPosition(node, rect.left, rect.top, rect.width, rect.height);
|
|
|
|
}
|
2013-03-04 00:57:42 +00:00
|
|
|
}
|
2013-06-05 19:34:15 +00:00
|
|
|
|
2013-07-12 16:10:01 +00:00
|
|
|
var visible = this.isNodeVisible(node);
|
|
|
|
throw new Capybara.UnpositionedElement(this.pathForNode(node), visible);
|
2013-01-21 00:13:58 +00:00
|
|
|
},
|
|
|
|
|
2017-01-04 00:33:42 +00:00
|
|
|
verifiedClickPosition: function (index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
var node = this.getNode(index);
|
2012-12-11 03:53:03 +00:00
|
|
|
node.scrollIntoViewIfNeeded();
|
2013-01-21 00:13:58 +00:00
|
|
|
var pos = this.clickPosition(node);
|
2013-11-09 20:12:16 +00:00
|
|
|
CapybaraInvocation.hover(pos.relativeX, pos.relativeY);
|
2013-06-05 19:34:15 +00:00
|
|
|
this.expectNodeAtPosition(node, pos);
|
2017-01-04 00:33:42 +00:00
|
|
|
return pos;
|
|
|
|
},
|
|
|
|
|
2018-01-06 20:26:31 +00:00
|
|
|
click: function (index, action, keys, offset) {
|
2017-01-04 00:33:42 +00:00
|
|
|
var pos = this.verifiedClickPosition(index);
|
2018-01-06 20:26:31 +00:00
|
|
|
keys = keys ? JSON.parse(keys) : [];
|
|
|
|
offset = offset ? JSON.parse(offset) : {};
|
|
|
|
if (offset && (offset.x != null) && (offset.y != null)){
|
|
|
|
action(pos.absoluteLeft + offset.x, pos.absoluteTop + offset.y, keys);
|
|
|
|
} else {
|
|
|
|
action(pos.absoluteX, pos.absoluteY, keys);
|
|
|
|
}
|
2011-02-26 20:50:45 +00:00
|
|
|
},
|
|
|
|
|
2018-01-06 20:26:31 +00:00
|
|
|
leftClick: function (index, keys, offset) {
|
|
|
|
this.click(index, CapybaraInvocation.leftClick, keys, offset);
|
2013-02-25 09:28:43 +00:00
|
|
|
},
|
|
|
|
|
2018-01-06 20:26:31 +00:00
|
|
|
doubleClick: function(index, keys, offset) {
|
|
|
|
this.click(index, CapybaraInvocation.leftClick, keys, offset);
|
|
|
|
this.click(index, CapybaraInvocation.doubleClick, keys, offset);
|
2013-02-25 09:28:43 +00:00
|
|
|
},
|
|
|
|
|
2018-01-06 20:26:31 +00:00
|
|
|
rightClick: function(index, keys, offset) {
|
|
|
|
this.click(index, CapybaraInvocation.rightClick, keys, offset);
|
2013-02-25 09:28:43 +00:00
|
|
|
},
|
|
|
|
|
2013-02-26 19:32:03 +00:00
|
|
|
hover: function (index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
var node = this.getNode(index);
|
2013-02-26 19:32:03 +00:00
|
|
|
node.scrollIntoViewIfNeeded();
|
|
|
|
|
|
|
|
var pos = this.clickPosition(node);
|
2013-06-05 19:34:15 +00:00
|
|
|
CapybaraInvocation.hover(pos.absoluteX, pos.absoluteY);
|
2013-02-26 19:32:03 +00:00
|
|
|
},
|
|
|
|
|
2011-02-26 20:50:45 +00:00
|
|
|
trigger: function (index, eventName) {
|
2015-05-22 17:41:01 +00:00
|
|
|
this.triggerOnNode(this.getNode(index), eventName);
|
|
|
|
},
|
|
|
|
|
|
|
|
triggerOnNode: function(node, eventName) {
|
2011-02-26 20:50:45 +00:00
|
|
|
var eventObject = document.createEvent("HTMLEvents");
|
|
|
|
eventObject.initEvent(eventName, true, true);
|
2015-05-22 17:41:01 +00:00
|
|
|
node.dispatchEvent(eventObject);
|
2011-02-26 21:20:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
visible: function (index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.isNodeVisible(this.getNode(index));
|
2013-07-12 16:10:01 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
isNodeVisible: function(node) {
|
2018-01-05 01:38:22 +00:00
|
|
|
var style = node.ownerDocument.defaultView.getComputedStyle(node, null);
|
|
|
|
// Only check computed visibility style on current node since it
|
|
|
|
// will inherit from nearest ancestor with a setting and overrides
|
|
|
|
// any farther ancestors
|
|
|
|
if (style.getPropertyValue('visibility') == 'hidden' || style.getPropertyValue('display') == 'none')
|
|
|
|
return false;
|
2012-10-23 05:33:03 +00:00
|
|
|
|
2018-01-05 01:38:22 +00:00
|
|
|
// Must check CSS display setting for all ancestors
|
|
|
|
while (node = node.parentElement) {
|
|
|
|
style = node.ownerDocument.defaultView.getComputedStyle(node, null);
|
|
|
|
if (style.getPropertyValue('display') == 'none' )
|
|
|
|
return false;
|
2011-02-26 21:20:05 +00:00
|
|
|
}
|
|
|
|
return true;
|
2011-02-26 20:48:44 +00:00
|
|
|
},
|
|
|
|
|
2011-07-29 15:18:51 +00:00
|
|
|
selected: function (index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.getNode(index).selected;
|
2011-07-29 15:18:51 +00:00
|
|
|
},
|
|
|
|
|
2011-02-26 20:48:44 +00:00
|
|
|
value: function(index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.getNode(index).value;
|
2011-02-26 21:26:22 +00:00
|
|
|
},
|
|
|
|
|
2012-09-28 14:27:43 +00:00
|
|
|
getInnerHTML: function(index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.getNode(index).innerHTML;
|
2012-09-27 14:27:04 +00:00
|
|
|
},
|
2012-09-26 21:30:35 +00:00
|
|
|
|
2012-09-28 14:27:43 +00:00
|
|
|
setInnerHTML: function(index, value) {
|
2013-12-06 15:43:17 +00:00
|
|
|
this.getNode(index).innerHTML = value;
|
2012-09-26 21:30:35 +00:00
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
2017-02-07 01:29:11 +00:00
|
|
|
sendKeys: function (elem_index, json_keys) {
|
|
|
|
var idx, length, keys;
|
|
|
|
keys = JSON.parse(json_keys);
|
2016-01-28 16:52:34 +00:00
|
|
|
length = keys.length;
|
|
|
|
|
|
|
|
if (length) {
|
2017-02-07 01:29:11 +00:00
|
|
|
this.focus(elem_index);
|
2016-01-28 16:52:34 +00:00
|
|
|
}
|
|
|
|
|
2017-02-07 01:29:11 +00:00
|
|
|
for (idx = 0; idx < length; idx++) {
|
|
|
|
this._sendKeys(keys[idx]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_sendKeys: function(keys) {
|
|
|
|
if (typeof keys == "string") {
|
|
|
|
var str_len = keys.length;
|
|
|
|
var str_idx;
|
|
|
|
for (str_idx = 0; str_idx < str_len; str_idx++) {
|
|
|
|
CapybaraInvocation.keypress(keys[str_idx]);
|
|
|
|
}
|
|
|
|
} else if (Array.isArray(keys)) {
|
|
|
|
this.keyModifiersStack.push([]);
|
|
|
|
var idx;
|
|
|
|
for (idx = 0; idx < keys.length; idx++) {
|
|
|
|
this._sendKeys(keys[idx]);
|
|
|
|
}
|
|
|
|
var mods = this.keyModifiersStack.pop();
|
|
|
|
while (mods.length) {
|
|
|
|
CapybaraInvocation.namedKeyup(mods.pop().key);
|
|
|
|
}
|
|
|
|
} else {
|
2017-03-17 20:47:25 +00:00
|
|
|
key = keys.key;
|
2017-02-07 01:29:11 +00:00
|
|
|
if (["Shift", "Control", "Alt", "Meta"].indexOf(key) > -1){
|
|
|
|
CapybaraInvocation.namedKeydown(key);
|
|
|
|
this.keyModifiersStack[this.keyModifiersStack.length-1].push(keys);
|
|
|
|
} else {
|
|
|
|
CapybaraInvocation.namedKeypress(key, keys.modifier);
|
|
|
|
}
|
2016-01-28 16:52:34 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-01-19 03:51:20 +00:00
|
|
|
set: function (index, value) {
|
|
|
|
var length, maxLength, node, strindex, textTypes, type;
|
|
|
|
|
2013-12-06 15:43:17 +00:00
|
|
|
node = this.getNode(index);
|
2012-01-19 03:51:20 +00:00
|
|
|
type = (node.type || node.tagName).toLowerCase();
|
|
|
|
textTypes = ["email", "number", "password", "search", "tel", "text", "textarea", "url"];
|
|
|
|
|
|
|
|
if (textTypes.indexOf(type) != -1) {
|
|
|
|
maxLength = this.attribute(index, "maxlength");
|
2011-08-25 17:37:14 +00:00
|
|
|
if (maxLength && value.length > maxLength) {
|
|
|
|
length = maxLength;
|
|
|
|
} else {
|
|
|
|
length = value.length;
|
|
|
|
}
|
|
|
|
|
2014-07-09 03:55:06 +00:00
|
|
|
if (!node.readOnly) {
|
|
|
|
this.focus(index);
|
|
|
|
|
2013-03-21 03:54:39 +00:00
|
|
|
node.value = "";
|
|
|
|
|
2014-07-09 03:55:06 +00:00
|
|
|
for (strindex = 0; strindex < length; strindex++) {
|
|
|
|
CapybaraInvocation.keypress(value[strindex]);
|
|
|
|
}
|
2012-01-19 03:51:20 +00:00
|
|
|
|
2016-01-28 16:52:34 +00:00
|
|
|
if (value === "") {
|
2014-07-09 03:55:06 +00:00
|
|
|
this.trigger(index, "change");
|
2016-01-28 16:52:34 +00:00
|
|
|
}
|
2014-07-09 03:55:06 +00:00
|
|
|
}
|
2012-01-19 03:51:20 +00:00
|
|
|
} else if (type === "checkbox" || type === "radio") {
|
2012-01-27 23:03:38 +00:00
|
|
|
if (node.checked != (value === "true")) {
|
2013-02-25 09:28:43 +00:00
|
|
|
this.leftClick(index);
|
2012-01-27 23:03:38 +00:00
|
|
|
}
|
2012-01-19 03:51:20 +00:00
|
|
|
} else if (type === "file") {
|
2012-11-18 07:48:24 +00:00
|
|
|
this.attachedFiles = Array.prototype.slice.call(arguments, 1);
|
2013-02-25 09:28:43 +00:00
|
|
|
this.leftClick(index);
|
2014-06-18 22:00:34 +00:00
|
|
|
} else if (this.isContentEditable(node)) {
|
|
|
|
var content = document.createTextNode(value);
|
|
|
|
node.innerHTML = '';
|
|
|
|
node.appendChild(content);
|
2011-03-11 16:25:06 +00:00
|
|
|
} else {
|
|
|
|
node.value = value;
|
|
|
|
}
|
2011-02-26 22:51:47 +00:00
|
|
|
},
|
|
|
|
|
2014-06-18 22:00:34 +00:00
|
|
|
isContentEditable: function(node) {
|
|
|
|
if (node.contentEditable == 'true') {
|
|
|
|
return true;
|
|
|
|
} else if (node.contentEditable == 'false') {
|
|
|
|
return false;
|
|
|
|
} else if (node.contentEditable == 'inherit') {
|
|
|
|
return this.isContentEditable(node.parentNode);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-07-10 00:34:09 +00:00
|
|
|
focus: function(index) {
|
2013-12-06 15:43:17 +00:00
|
|
|
this.getNode(index).focus();
|
2012-07-10 00:34:09 +00:00
|
|
|
},
|
|
|
|
|
2017-02-17 02:32:40 +00:00
|
|
|
focus_frame: function(index) {
|
|
|
|
var elem = this.getNode(index);
|
|
|
|
if (elem === document.activeElement) {
|
|
|
|
elem.blur();
|
|
|
|
}
|
|
|
|
elem.focus();
|
|
|
|
},
|
|
|
|
|
2017-11-03 01:33:43 +00:00
|
|
|
selectOption: function(index){
|
|
|
|
this._setOption(index, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
unselectOption: function(index){
|
|
|
|
this._setOption(index, false);
|
|
|
|
},
|
|
|
|
|
|
|
|
_setOption: function(index, state) {
|
2015-05-22 17:41:01 +00:00
|
|
|
var optionNode = this.getNode(index);
|
|
|
|
var selectNode = optionNode.parentNode;
|
2016-12-14 16:32:07 +00:00
|
|
|
if (selectNode.tagName == "OPTGROUP")
|
|
|
|
selectNode = selectNode.parentNode;
|
2015-05-22 17:41:01 +00:00
|
|
|
|
2015-06-28 21:19:55 +00:00
|
|
|
if (optionNode.disabled)
|
|
|
|
return;
|
|
|
|
|
2017-11-03 01:33:43 +00:00
|
|
|
if ((!selectNode.multiple) && (!state))
|
|
|
|
return;
|
|
|
|
|
2015-05-22 17:41:01 +00:00
|
|
|
// click on select list
|
|
|
|
this.triggerOnNode(selectNode, 'mousedown');
|
|
|
|
selectNode.focus();
|
2017-11-03 01:33:43 +00:00
|
|
|
this.triggerOnNode(selectNode, 'input');
|
2015-05-22 17:41:01 +00:00
|
|
|
|
2017-11-03 01:33:43 +00:00
|
|
|
// select/deselect option from list
|
|
|
|
if (optionNode.selected != state){
|
|
|
|
optionNode.selected = state;
|
|
|
|
this.triggerOnNode(selectNode, 'change');
|
|
|
|
}
|
2011-02-26 22:51:47 +00:00
|
|
|
|
2017-11-03 01:33:43 +00:00
|
|
|
this.triggerOnNode(selectNode, 'mouseup');
|
|
|
|
this.triggerOnNode(selectNode, 'click');
|
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));
|
2012-07-10 00:34:09 +00:00
|
|
|
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];
|
|
|
|
}
|
2012-07-10 00:34:09 +00:00
|
|
|
// 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) {
|
2013-12-06 15:43:17 +00:00
|
|
|
var element = this.getNode(index), target = this.getNode(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);
|
2012-07-10 00:34:09 +00:00
|
|
|
};
|
2011-04-23 11:22:16 +00:00
|
|
|
mouseTrigger('mousedown', options);
|
2012-07-10 00:34:09 +00:00
|
|
|
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);
|
2012-07-10 00:34:09 +00:00
|
|
|
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
|
|
|
},
|
2017-01-27 21:48:04 +00:00
|
|
|
registerNode: function(node) {
|
|
|
|
this.nextIndex++;
|
|
|
|
this.nodes[this.nextIndex] = node;
|
|
|
|
return this.nextIndex;
|
|
|
|
},
|
2012-11-18 00:30:29 +00:00
|
|
|
equals: function(index, targetIndex) {
|
2013-12-06 15:43:17 +00:00
|
|
|
return this.getNode(index) === this.getNode(targetIndex);
|
2017-01-27 21:48:04 +00:00
|
|
|
},
|
|
|
|
_visitedObjects: [],
|
|
|
|
wrapResult: function(arg) {
|
|
|
|
if (this._visitedObjects.indexOf(arg) >= 0) { return '(cyclic structure)'; }
|
|
|
|
if (arg instanceof NodeList) { arg = Array.prototype.slice.call(arg, 0); }
|
|
|
|
if (Array.isArray(arg)) {
|
|
|
|
for(var _j = 0; _j < arg.length; _j++) {
|
|
|
|
arg[_j] = this.wrapResult(arg[_j]);
|
|
|
|
}
|
|
|
|
} else if (arg && arg.nodeType == 1 && arg.tagName) {
|
2017-03-17 20:47:25 +00:00
|
|
|
return {'element-581e-422e-8be1-884c4e116226': this.registerNode(arg)};
|
2017-01-27 21:48:04 +00:00
|
|
|
} else if (arg === null) {
|
|
|
|
return undefined;
|
2017-10-20 17:06:19 +00:00
|
|
|
} else if (arg instanceof Date){
|
|
|
|
return arg;
|
2017-01-27 21:48:04 +00:00
|
|
|
} else if ( typeof arg == 'object' ) {
|
|
|
|
this._visitedObjects.push(arg);
|
2017-10-20 17:06:19 +00:00
|
|
|
var result = {};
|
2017-01-27 21:48:04 +00:00
|
|
|
for(var _k in arg){
|
2017-10-20 17:06:19 +00:00
|
|
|
result[_k] = this.wrapResult(arg[_k]);
|
2017-01-27 21:48:04 +00:00
|
|
|
}
|
|
|
|
this._visitedObjects.pop();
|
2017-10-20 17:06:19 +00:00
|
|
|
return result;
|
2017-01-27 21:48:04 +00:00
|
|
|
}
|
|
|
|
return arg;
|
2011-04-23 11:22:16 +00:00
|
|
|
}
|
2011-02-26 03:32:22 +00:00
|
|
|
};
|
|
|
|
|
2013-06-05 19:34:15 +00:00
|
|
|
Capybara.ClickFailed = function(expectedPath, actualPath, position) {
|
2013-01-21 00:13:58 +00:00
|
|
|
this.name = 'Capybara.ClickFailed';
|
2013-06-05 19:34:15 +00:00
|
|
|
this.message = 'Failed to click element ' + expectedPath;
|
|
|
|
if (actualPath)
|
|
|
|
this.message += ' because of overlapping element ' + actualPath;
|
2013-02-10 02:04:51 +00:00
|
|
|
if (position)
|
|
|
|
this.message += ' at position ' + position["absoluteX"] + ', ' + position["absoluteY"];
|
|
|
|
else
|
|
|
|
this.message += ' at unknown position';
|
2013-11-09 19:34:43 +00:00
|
|
|
this.message += "; \nA screenshot of the page at the time of the failure has been written to " + CapybaraInvocation.render();
|
2013-01-21 00:13:58 +00:00
|
|
|
};
|
|
|
|
Capybara.ClickFailed.prototype = new Error();
|
|
|
|
Capybara.ClickFailed.prototype.constructor = Capybara.ClickFailed;
|
2013-06-05 19:34:15 +00:00
|
|
|
|
2013-07-12 16:10:01 +00:00
|
|
|
Capybara.UnpositionedElement = function(path, visible) {
|
2013-06-05 19:34:15 +00:00
|
|
|
this.name = 'Capybara.ClickFailed';
|
|
|
|
this.message = 'Failed to find position for element ' + path;
|
2013-07-12 16:10:01 +00:00
|
|
|
if (!visible)
|
|
|
|
this.message += ' because it is not visible';
|
2013-06-05 19:34:15 +00:00
|
|
|
};
|
|
|
|
Capybara.UnpositionedElement.prototype = new Error();
|
|
|
|
Capybara.UnpositionedElement.prototype.constructor = Capybara.UnpositionedElement;
|
2013-12-06 15:43:17 +00:00
|
|
|
|
|
|
|
Capybara.NodeNotAttachedError = function(index) {
|
|
|
|
this.name = 'Capybara.NodeNotAttachedError';
|
|
|
|
this.message = 'Element at ' + index + ' no longer present in the DOM';
|
|
|
|
};
|
|
|
|
Capybara.NodeNotAttachedError.prototype = new Error();
|
|
|
|
Capybara.NodeNotAttachedError.prototype.constructor = Capybara.NodeNotAttachedError;
|