2012-11-16 04:48:15 +04:00
|
|
|
/*
|
2012-11-19 03:58:51 +04:00
|
|
|
This file is part of the GhostDriver by Ivan De Marino <http://ivandemarino.me>.
|
2012-11-16 04:48:15 +04:00
|
|
|
|
2014-07-24 21:25:46 +04:00
|
|
|
Copyright (c) 2012-2014, Ivan De Marino <http://ivandemarino.me>
|
2012-11-16 04:48:15 +04:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright notice,
|
|
|
|
this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
this list of conditions and the following disclaimer in the documentation
|
|
|
|
and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
|
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var ghostdriver = ghostdriver || {};
|
|
|
|
|
|
|
|
ghostdriver.WebElementLocator = function(session) {
|
|
|
|
// private:
|
2013-03-18 05:30:24 +04:00
|
|
|
const
|
2012-11-16 04:48:15 +04:00
|
|
|
_supportedStrategies = [
|
|
|
|
"class name", "className", //< Returns an element whose class name contains the search value; compound class names are not permitted.
|
|
|
|
"css", "css selector", //< Returns an element matching a CSS selector.
|
|
|
|
"id", //< Returns an element whose ID attribute matches the search value.
|
|
|
|
"name", //< Returns an element whose NAME attribute matches the search value.
|
|
|
|
"link text", "linkText", //< Returns an anchor element whose visible text matches the search value.
|
|
|
|
"partial link text", "partialLinkText", //< Returns an anchor element whose visible text partially matches the search value.
|
|
|
|
"tag name", "tagName", //< Returns an element whose tag name matches the search value.
|
|
|
|
"xpath" //< Returns an element matching an XPath expression.
|
2013-03-18 05:30:24 +04:00
|
|
|
];
|
|
|
|
|
|
|
|
var
|
2012-11-16 04:48:15 +04:00
|
|
|
_session = session,
|
|
|
|
_errors = require("./errors.js"),
|
2013-03-18 05:30:24 +04:00
|
|
|
_log = ghostdriver.logger.create("WebElementLocator"),
|
2012-11-16 04:48:15 +04:00
|
|
|
|
|
|
|
_find = function(what, locator, rootElement) {
|
|
|
|
var currWindow = _session.getCurrentWindow(),
|
|
|
|
findRes,
|
|
|
|
findAtom = require("./webdriver_atoms.js").get(
|
|
|
|
"find_" +
|
|
|
|
(what.indexOf("element") >= 0 ? what : "element")), //< normalize
|
|
|
|
errorMsg;
|
|
|
|
|
|
|
|
if (currWindow !== null &&
|
|
|
|
locator && typeof(locator) === "object" &&
|
|
|
|
locator.using && locator.value && //< if well-formed input
|
|
|
|
_supportedStrategies.indexOf(locator.using) >= 0) { //< and if strategy is recognized
|
|
|
|
|
2014-01-04 21:20:21 +04:00
|
|
|
_log.debug("_find.locator", JSON.stringify(locator));
|
2012-11-16 04:48:15 +04:00
|
|
|
|
|
|
|
// Ensure "rootElement" is valid, otherwise undefine-it
|
|
|
|
if (!rootElement || typeof(rootElement) !== "object" || !rootElement["ELEMENT"]) {
|
|
|
|
rootElement = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use Atom "find_result" to search for element in the page
|
|
|
|
findRes = currWindow.evaluate(
|
|
|
|
findAtom,
|
|
|
|
locator.using,
|
|
|
|
locator.value,
|
|
|
|
rootElement);
|
|
|
|
|
|
|
|
// De-serialise the result of the Atom execution
|
|
|
|
try {
|
|
|
|
return JSON.parse(findRes);
|
|
|
|
} catch (e) {
|
2014-01-04 21:20:21 +04:00
|
|
|
errorMsg = JSON.stringify(locator);
|
|
|
|
_log.error("_find.locator.error", errorMsg);
|
2012-11-16 04:48:15 +04:00
|
|
|
return {
|
2014-07-24 21:25:46 +04:00
|
|
|
"status" : _errors.FAILED_CMD_STATUS_CODES.UnknownCommand,
|
2012-11-16 04:48:15 +04:00
|
|
|
"value" : errorMsg
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Window was not found
|
|
|
|
return {
|
2014-07-24 21:25:46 +04:00
|
|
|
"status" : _errors.FAILED_CMD_STATUS_CODES.NoSuchWindow,
|
2012-11-16 04:48:15 +04:00
|
|
|
"value" : "No such window"
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
_locateElement = function(locator, rootElement) {
|
|
|
|
var findElementRes = _find("element", locator, rootElement);
|
|
|
|
|
2014-01-04 21:20:21 +04:00
|
|
|
_log.debug("_locateElement.locator", JSON.stringify(locator));
|
|
|
|
_log.debug("_locateElement.findElementResult", JSON.stringify(findElementRes));
|
2012-11-16 04:48:15 +04:00
|
|
|
|
2013-07-26 02:24:53 +04:00
|
|
|
// To check if element was found, the following must happen:
|
|
|
|
// 1. "findElementRes" result object must be valid
|
|
|
|
// 2. property "status" is found and is {Number}
|
|
|
|
if (findElementRes !== null && typeof(findElementRes) === "object" &&
|
|
|
|
findElementRes.hasOwnProperty("status") && typeof(findElementRes.status) === "number") {
|
2012-11-16 04:48:15 +04:00
|
|
|
// If the atom succeeds, but returns a null value, the element was not found.
|
|
|
|
if (findElementRes.status === 0 && findElementRes.value === null) {
|
2014-07-24 21:25:46 +04:00
|
|
|
findElementRes.status = _errors.FAILED_CMD_STATUS_CODES.NoSuchElement;
|
2012-11-16 04:48:15 +04:00
|
|
|
findElementRes.value = {
|
|
|
|
"message": "Unable to find element with " +
|
|
|
|
locator.using + " '" +
|
|
|
|
locator.value + "'"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return findElementRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not found
|
|
|
|
return {
|
2014-07-24 21:25:46 +04:00
|
|
|
"status" : _errors.FAILED_CMD_STATUS_CODES.NoSuchElement,
|
2012-11-16 04:48:15 +04:00
|
|
|
"value" : "No Such Element found"
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
_locateElements = function(locator, rootElement) {
|
|
|
|
var findElementsRes = _find("elements", locator, rootElement),
|
2014-07-24 21:25:46 +04:00
|
|
|
elements = [];
|
2012-11-16 04:48:15 +04:00
|
|
|
|
2014-01-04 21:20:21 +04:00
|
|
|
_log.debug("_locateElements.locator", JSON.stringify(locator));
|
|
|
|
_log.debug("_locateElements.findElementsResult", JSON.stringify(findElementsRes));
|
2012-11-16 04:48:15 +04:00
|
|
|
|
2013-07-26 02:24:53 +04:00
|
|
|
// To check if something was found, the following must happen:
|
|
|
|
// 1. "findElementsRes" result object must be valid
|
|
|
|
// 2. property "status" is found and is {Number}
|
|
|
|
// 3. property "value" is found and is and {Object}
|
|
|
|
if (findElementsRes !== null && typeof(findElementsRes) === "object" &&
|
|
|
|
findElementsRes.hasOwnProperty("status") && typeof(findElementsRes.status) === "number" &&
|
|
|
|
findElementsRes.hasOwnProperty("value") && findElementsRes.value !== null && typeof(findElementsRes.value) === "object") {
|
2012-11-16 04:48:15 +04:00
|
|
|
return findElementsRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not found
|
|
|
|
return {
|
2014-07-24 21:25:46 +04:00
|
|
|
"status" : _errors.FAILED_CMD_STATUS_CODES.NoSuchElement,
|
2012-11-16 04:48:15 +04:00
|
|
|
"value" : "No Such Elements found"
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
_locateActiveElement = function() {
|
|
|
|
var currWindow = _session.getCurrentWindow(),
|
|
|
|
activeElementRes;
|
|
|
|
|
|
|
|
if (currWindow !== null) {
|
|
|
|
activeElementRes = currWindow.evaluate(
|
|
|
|
require("./webdriver_atoms.js").get("active_element"));
|
|
|
|
|
|
|
|
// De-serialise the result of the Atom execution
|
|
|
|
try {
|
|
|
|
activeElementRes = JSON.parse(activeElementRes);
|
|
|
|
} catch (e) {
|
|
|
|
return {
|
2014-07-24 21:25:46 +04:00
|
|
|
"status" : _errors.FAILED_CMD_STATUS_CODES.NoSuchElement,
|
2012-11-16 04:48:15 +04:00
|
|
|
"value" : "No Active Element found"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// If found
|
|
|
|
if (typeof(activeElementRes.status) !== "undefined") {
|
|
|
|
return activeElementRes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2014-07-24 21:25:46 +04:00
|
|
|
"status" : _errors.FAILED_CMD_STATUS_CODES.NoSuchWindow,
|
2012-11-16 04:48:15 +04:00
|
|
|
"value" : "No such window"
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
_handleLocateCommand = function(req, res, locatorMethod, rootElement, startTime) {
|
|
|
|
// Search for a WebElement on the Page
|
|
|
|
var elementOrElements,
|
|
|
|
searchStartTime = startTime || new Date().getTime(),
|
|
|
|
stopSearchByTime,
|
|
|
|
request = {};
|
|
|
|
|
2013-07-26 02:24:53 +04:00
|
|
|
_log.debug("_handleLocateCommand", "Element(s) Search Start Time: " + searchStartTime);
|
|
|
|
|
2012-11-16 04:48:15 +04:00
|
|
|
// If a "locatorMethod" was not provided, default to "locateElement"
|
|
|
|
if(typeof(locatorMethod) !== "function") {
|
2014-07-24 21:25:46 +04:00
|
|
|
locatorMethod = _locateElement;
|
2012-11-16 04:48:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Some language bindings can send a null instead of an empty
|
|
|
|
// JSON object for the getActiveElement command.
|
|
|
|
if (req.post && typeof req.post === "string") {
|
|
|
|
request = JSON.parse(req.post);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to find the element
|
|
|
|
elementOrElements = locatorMethod(request, rootElement);
|
|
|
|
|
2014-01-04 21:20:21 +04:00
|
|
|
_log.debug("_handleLocateCommand.elements", JSON.stringify(elementOrElements));
|
|
|
|
_log.debug("_handleLocateCommand.rootElement", (typeof(rootElement) !== "undefined" ? JSON.stringify(rootElement) : "BODY"));
|
2012-11-16 04:48:15 +04:00
|
|
|
|
|
|
|
if (elementOrElements &&
|
|
|
|
elementOrElements.hasOwnProperty("status") &&
|
|
|
|
elementOrElements.status === 0 &&
|
|
|
|
elementOrElements.hasOwnProperty("value")) {
|
|
|
|
|
|
|
|
// return if elements found OR we passed the "stopSearchByTime"
|
2012-12-15 23:00:23 +04:00
|
|
|
stopSearchByTime = searchStartTime + _session.getImplicitTimeout();
|
2012-11-16 04:48:15 +04:00
|
|
|
if (elementOrElements.value.length !== 0 || new Date().getTime() > stopSearchByTime) {
|
2013-07-26 02:24:53 +04:00
|
|
|
|
|
|
|
_log.debug("_handleLocateCommand", "Element(s) Found. Search Stop Time: " + stopSearchByTime);
|
|
|
|
|
2012-11-16 04:48:15 +04:00
|
|
|
res.success(_session.getId(), elementOrElements.value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// retry if we haven't passed "stopSearchByTime"
|
2012-12-15 23:00:23 +04:00
|
|
|
stopSearchByTime = searchStartTime + _session.getImplicitTimeout();
|
2012-11-16 04:48:15 +04:00
|
|
|
if (stopSearchByTime >= new Date().getTime()) {
|
2013-07-26 02:24:53 +04:00
|
|
|
|
|
|
|
_log.debug("_handleLocateCommand", "Element(s) NOT Found: RETRY. Search Stop Time: " + stopSearchByTime);
|
|
|
|
|
2012-11-16 04:48:15 +04:00
|
|
|
// Recursive call in 50ms
|
|
|
|
setTimeout(function(){
|
|
|
|
_handleLocateCommand(req, res, locatorMethod, rootElement, searchStartTime);
|
|
|
|
}, 50);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error handler. We got a valid response, but it was an error response.
|
|
|
|
if (elementOrElements) {
|
2013-07-26 02:24:53 +04:00
|
|
|
|
|
|
|
_log.error("_handleLocateCommand", "Element(s) NOT Found: GAVE UP. Search Stop Time: " + stopSearchByTime);
|
|
|
|
|
2014-07-24 21:25:46 +04:00
|
|
|
_errors.handleFailedCommandEH(elementOrElements.status,
|
2012-11-16 04:48:15 +04:00
|
|
|
elementOrElements.value.message,
|
|
|
|
req,
|
|
|
|
res,
|
2014-07-24 21:25:46 +04:00
|
|
|
_session);
|
2012-11-16 04:48:15 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw _errors.createInvalidReqVariableResourceNotFoundEH(req);
|
|
|
|
};
|
|
|
|
|
|
|
|
// public:
|
|
|
|
return {
|
|
|
|
locateElement : _locateElement,
|
|
|
|
locateElements : _locateElements,
|
|
|
|
locateActiveElement : _locateActiveElement,
|
|
|
|
handleLocateCommand : _handleLocateCommand
|
|
|
|
};
|
|
|
|
};
|