Ivan De Marino 2012-12-15 19:00:23 +00:00 committed by Ariya Hidayat
parent f5652e5110
commit 027aa93b18
7 changed files with 219 additions and 46 deletions

View File

@ -1,10 +1,7 @@
2012-11-25 20:54:35
2012-12-15 18:59:39
commit 54876580d0a3886e42850804b03184adbd899d65 (HEAD, tag: refs/tags/1.0.0, refs/remotes/origin/master, refs/remotes/origin/HEAD, refs/heads/master)
commit 2676fd4e6410202ec439a235316451ea2c633681 (HEAD, tag: refs/tags/1.0.1, refs/heads/master)
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date: Sun Nov 25 19:05:15 2012 +0000
Date: Sat Dec 15 18:58:34 2012 +0000
Updated export script.
1. Creates the `README.md` to inform PhantomJS developers that the code is automatically exported from GhostDriver
2. Adds `--decorate=full` to the line where we populate the `lastupdate` file
Preparing GhostDriver version `1.0.1`.

View File

@ -30,7 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var ghostdriver = {
system : require('system'),
hub : require('./hub_register'),
version : "1.0.0"
version : "1.0.1"
},
server = require('webserver').create(),
router,

View File

@ -345,7 +345,7 @@ ghostdriver.SessionReqHand = function(session) {
var postObj = JSON.parse(req.post),
result,
timer,
scriptTimeout = _session.getTimeout(_session.timeoutNames().SCRIPT),
scriptTimeout = _session.getScriptTimeout(),
timedOut = false;
if (typeof(postObj) === "object" && postObj.script && postObj.args) {
@ -355,7 +355,7 @@ ghostdriver.SessionReqHand = function(session) {
timedOut = true;
_errors.handleFailedCommandEH(
_errors.FAILED_CMD_STATUS.TIMEOUT,
"Script didn't return within "+scriptTimeout+"ms",
"Script didn't return within " + scriptTimeout + "ms",
req,
res,
_session,
@ -399,14 +399,21 @@ ghostdriver.SessionReqHand = function(session) {
"}",
postObj.script,
postObj.args,
_session.getTimeout(_session.timeoutNames().ASYNC_SCRIPT));
_session.getAsyncScriptTimeout());
} else {
throw _errors.createInvalidReqMissingCommandParameterEH(req);
}
},
_getWindowHandle = function (req, res) {
var handle = _session.getCurrentWindowHandle();
var handle;
// Initialize the Current Window (we need at least that)
_session.initCurrentWindowIfNull();
// Get current window handle
handle = _session.getCurrentWindowHandle();
if (handle !== null) {
res.success(_session.getId(), handle);
} else {
@ -420,6 +427,8 @@ ghostdriver.SessionReqHand = function(session) {
},
_getWindowHandles = function(req, res) {
// Initialize the Current Window (we need at least that)
_session.initCurrentWindowIfNull();
res.success(_session.getId(), _session.getWindowHandles());
},
@ -460,9 +469,7 @@ ghostdriver.SessionReqHand = function(session) {
// Request timed out
_errors.handleFailedCommandEH(
_errors.FAILED_CMD_STATUS.TIMEOUT,
"URL '" + postObj.url + "' didn't load within " +
_session.getTimeout(_session.timeoutNames().PAGE_LOAD) +
"ms",
"URL '" + postObj.url + "' didn't load within " + _session.getPageLoadTimeout() + "ms",
req,
res,
_session,
@ -478,13 +485,28 @@ ghostdriver.SessionReqHand = function(session) {
// Normalize the call: the "type" is read from the URL, not a POST parameter
if (req.urlParsed.file === _const.IMPLICIT_WAIT) {
postObj["type"] = _session.timeoutNames().IMPLICIT;
postObj["type"] = _session.timeoutNames.IMPLICIT;
} else if (req.urlParsed.file === _const.ASYNC_SCRIPT) {
postObj["type"] = _session.timeoutNames().ASYNC_SCRIPT;
postObj["type"] = _session.timeoutNames.ASYNC_SCRIPT;
}
if (typeof(postObj["type"]) !== "undefined" && typeof(postObj["ms"]) !== "undefined") {
_session.setTimeout(postObj["type"], postObj["ms"]);
// Set the right timeout on the Session
switch(postObj["type"]) {
case _session.timeoutNames.SCRIPT:
_session.setScriptTimeout(postObj["ms"]);
break;
case _session.timeoutNames.ASYNC_SCRIPT:
_session.setAsyncScriptTimeout(postObj["ms"]);
break;
case _session.timeoutNames.IMPLICIT:
_session.setImplicitTimeout(postObj["ms"]);
break;
case _session.timeoutNames.PAGE_LOAD:
_session.setPageLoadTimeout(postObj["ms"]);
break;
}
res.success(_session.getId());
} else {
throw _errors.createInvalidReqMissingCommandParameterEH(req);

View File

@ -34,10 +34,10 @@ ghostdriver.ShutdownReqHand = function() {
_protoParent = ghostdriver.ShutdownReqHand.prototype,
_handle = function(req, res) {
_protoParent.handle.call(this, req, res);
_protoParent.handle.call(this, req, res);
if (req.method === "GET" && req.urlParsed.file === "shutdown") {
//res.success(null, null);
// Any HTTP Request Method will be accepted for this command. Some drivers like HEAD for example...
if (req.urlParsed.file === "shutdown") {
res.statusCode = 200;
res.setHeader("Content-Type", "text/html;charset=UTF-8");
res.setHeader("Content-Length", 36);

View File

@ -348,7 +348,8 @@ ghostdriver.WebElementReqHand = function(idOrElement, session) {
require("./webdriver_atoms.js").get("execute_script"),
"return arguments[0].isSameNode(arguments[1]);",
[_getJSON(), _getJSON(req.urlParsed.file)]);
res.success(_session.getId(), result);
res.respondBasedOnResult(_session, req, result);
return;
}
@ -378,7 +379,7 @@ ghostdriver.WebElementReqHand = function(idOrElement, session) {
} else {
_errors.handleFailedCommandEH(
_errors.FAILED_CMD_STATUS.UNKNOWN_ERROR,
"Submit succeded but Load Failed",
"Submit succeeded but Load Failed",
req,
res,
_session,
@ -401,12 +402,72 @@ ghostdriver.WebElementReqHand = function(idOrElement, session) {
});
},
_postClickCommand = function(req, res) {
var result = _protoParent.getSessionCurrWindow.call(this, _session, req).evaluate(
require("./webdriver_atoms.js").get("click"),
_getJSON());
_canCausePageLoadOnClick = function(currWindow) {
var tagName = _getTagName(currWindow).toLowerCase(),
href = _getAttribute(currWindow, "href"),
type = _getAttribute(currWindow, "type").toLowerCase();
res.respondBasedOnResult(_session, req, result);
// Return "true" if it's an element that "can cause a page load if clicked"
if (tagName === "a" && !!href && href.length > 0) {
return true;
}
if (tagName === "input" && type === "submit") {
return true;
}
return false;
},
_postClickCommand = function(req, res) {
var currWindow = _protoParent.getSessionCurrWindow.call(this, _session, req),
clickRes,
abortCallback = false;
if (_canCausePageLoadOnClick(currWindow)) {
// Clicking on Current Element can cause a page load, hence we need to wait for it to happen
currWindow.execFuncAndWaitForLoad(function() {
// do the click
clickRes = currWindow.evaluate(require("./webdriver_atoms.js").get("click"), _getJSON());
// If Click was NOT positive, status will be set to something else than '0'
clickRes = JSON.parse(clickRes);
if (clickRes && clickRes.status !== 0) {
abortCallback = true; //< handling the error here
res.respondBasedOnResult(_session, req, clickRes);
}
}, function(status) { //< onLoadFinished
// Report about the Load, only if it was not already handled
if (!abortCallback) {
if (status === "success") {
res.success(_session.getId());
} else {
_errors.handleFailedCommandEH(
_errors.FAILED_CMD_STATUS.UNKNOWN_ERROR,
"Click succeeded but Load Failed",
req,
res,
_session,
"WebElementReqHand");
}
}
}, function() {
if (arguments.length === 0) { //< onTimeout
// onclick didn't bubble up, but we should still return success
res.success(_session.getId());
} else { //< onError
_errors.handleFailedCommandEH(
_errors.FAILED_CMD_STATUS.UNKNOWN_ERROR,
"Click failed: " + arguments[0],
req,
res,
_session,
"WebElementReqHand");
}
});
} else {
// By default, clicking on this element can't cause a Page Load: we are done after having clicked
clickRes = currWindow.evaluate(require("./webdriver_atoms.js").get("click"), _getJSON());
res.respondBasedOnResult(_session, req, clickRes);
}
},
_getSelectedCommand = function(req, res) {

View File

@ -76,11 +76,15 @@ ghostdriver.Session = function(desiredCapabilities) {
_defaultCapabilities.proxy :
desiredCapabilities.proxy
},
// NOTE: This value is needed for Timeouts Upper-bound limit.
// "setTimeout/setInterval" accept only 32 bit integers, even though Number are all Doubles (go figure!)
// Interesting details here: {@link http://stackoverflow.com/a/4995054}.
_max32bitInt = Math.pow(2, 31) -1, //< Max 32bit Int
_timeouts = {
"script" : 500, //< 0.5s
"async script" : 5000, //< 5s
"script" : _max32bitInt,
"async script" : _max32bitInt,
"implicit" : 0, //< 0s
"page load" : 10000 //< 10s
"page load" : _max32bitInt,
},
_const = {
TIMEOUT_NAMES : {
@ -95,7 +99,23 @@ ghostdriver.Session = function(desiredCapabilities) {
_currentWindowHandle = null,
_id = require("./third_party/uuid.js").v1(),
_inputs = ghostdriver.Inputs(),
_capsPageSettingsPref = "phantomjs.page.settings.",
_pageSettings = {},
k, settingKey;
// Searching for `phantomjs.settings.*` in the Desired Capabilities and merging with the Negotiated Capabilities
// Possible values: @see https://github.com/ariya/phantomjs/wiki/API-Reference#wiki-webpage-settings.
for (k in desiredCapabilities) {
if (k.indexOf(_capsPageSettingsPref) === 0) {
settingKey = k.substring(_capsPageSettingsPref.length);
if (settingKey.length > 0) {
_negotiatedCapabilities[k] = desiredCapabilities[k];
_pageSettings[settingKey] = desiredCapabilities[k];
}
}
}
var
/**
* Executes a function and waits for Load to happen.
*
@ -110,6 +130,7 @@ ghostdriver.Session = function(desiredCapabilities) {
var args = Array.prototype.splice.call(arguments, 0),
timer,
loadingNewPage = false,
pageLoadNotTriggered = true,
thisPage = this;
// Normalize "execTypeOpt" value
@ -139,10 +160,12 @@ ghostdriver.Session = function(desiredCapabilities) {
// callback.
this.setOneShotCallback("onLoadStarted", function () {
// console.log("onLoadStarted");
pageLoadNotTriggered = false;
loadingNewPage = true;
});
this.setOneShotCallback("onUrlChanged", function () {
// console.log("onUrlChanged");
pageLoadNotTriggered = false;
// If "not loading a new page" it's just a fragment change
// and we should call "onLoadFunc()"
@ -168,6 +191,7 @@ ghostdriver.Session = function(desiredCapabilities) {
// message += " in " + item["function"];
// console.log(" " + message);
// });
pageLoadNotTriggered = false;
thisPage.stop(); //< stop the page from loading
clearTimeout(timer);
@ -177,12 +201,13 @@ ghostdriver.Session = function(desiredCapabilities) {
});
// Starting timer
// console.log("Setting timer to: " + _getPageLoadTimeout());
timer = setTimeout(function() {
thisPage.stop(); //< stop the page from loading
thisPage.resetOneShotCallbacks();
onErrorFunc.apply(thisPage, arguments);
}, _getTimeout(_const.TIMEOUT_NAMES.PAGE_LOAD));
}, _getPageLoadTimeout());
// We are ready to execute
if (execTypeOpt === "eval") {
@ -192,6 +217,15 @@ ghostdriver.Session = function(desiredCapabilities) {
// "Apply" the provided function
func.apply(this, args);
}
// In case a Page Load is not triggered at all (within 0.5s), we assume it's done and move on
setTimeout(function() {
if (pageLoadNotTriggered) {
clearTimeout(timer);
thisPage.resetOneShotCallbacks();
onLoadFunc.call(thisPage, "success");
}
}, 500);
},
_oneShotCallbackFactory = function(page, callbackName) {
@ -239,6 +273,8 @@ ghostdriver.Session = function(desiredCapabilities) {
},
_decorateNewWindow = function(page) {
var k;
// Decorating:
// 0. Pages lifetime will be managed by Driver, not the pages
page.ownsPages = false;
@ -258,8 +294,16 @@ ghostdriver.Session = function(desiredCapabilities) {
page.onPageCreated = _addNewPage;
// 5. Remove every closing page
page.onClosing = _deleteClosingPage;
// 6. Applying Page settings received via capabilities
for (k in _pageSettings) {
// Apply setting only if really supported by PhantomJS
if (p.settings.hasOwnProperty(k)) {
page.settings[k] = _pageSettings[k];
}
}
// page.onConsoleMessage = function(msg) { console.log(msg); };
// console.log("New Window/Page settings: " + JSON.stringify(page.settings, null, " "));
return page;
},
@ -284,14 +328,21 @@ ghostdriver.Session = function(desiredCapabilities) {
return page;
},
_getCurrentWindow = function() {
var page = null;
_initCurrentWindowIfNull = function() {
// Ensure a Current Window is available, if it's found to be `null`
if (_currentWindowHandle === null) {
// First call to get the current window: need to create one
page = _decorateNewWindow(require("webpage").create());
_currentWindowHandle = page.windowHandle;
_windows[_currentWindowHandle] = page;
} else if (_windows.hasOwnProperty(_currentWindowHandle)) {
}
},
_getCurrentWindow = function() {
var page = null;
_initCurrentWindowIfNull();
if (_windows.hasOwnProperty(_currentWindowHandle)) {
page = _windows[_currentWindowHandle];
}
@ -306,6 +357,8 @@ ghostdriver.Session = function(desiredCapabilities) {
if (page !== null) {
// Switch current window and return "true"
_currentWindowHandle = page.windowHandle;
// Switch to the Main Frame of that window
page.switchToMainFrame();
return true;
}
@ -353,15 +406,48 @@ ghostdriver.Session = function(desiredCapabilities) {
},
_setTimeout = function(type, ms) {
_timeouts[type] = ms;
// In case the chosen timeout is less than 0, we reset it to `_max32bitInt`
if (ms < 0) {
_timeouts[type] = _max32bitInt;
} else {
_timeouts[type] = ms;
}
},
_getTimeout = function(type) {
return _timeouts[type];
},
_timeoutNames = function() {
return _const.TIMEOUT_NAMES;
_getScriptTimeout = function() {
return _getTimeout(_const.TIMEOUT_NAMES.SCRIPT);
},
_getAsyncScriptTimeout = function() {
return _getTimeout(_const.TIMEOUT_NAMES.ASYNC_SCRIPT);
},
_getImplicitTimeout = function() {
return _getTimeout(_const.TIMEOUT_NAMES.IMPLICIT);
},
_getPageLoadTimeout = function() {
return _getTimeout(_const.TIMEOUT_NAMES.PAGE_LOAD);
},
_setScriptTimeout = function(ms) {
_setTimeout(_const.TIMEOUT_NAMES.SCRIPT, ms);
},
_setAsyncScriptTimeout = function(ms) {
_setTimeout(_const.TIMEOUT_NAMES.ASYNC_SCRIPT, ms);
},
_setImplicitTimeout = function(ms) {
_setTimeout(_const.TIMEOUT_NAMES.IMPLICIT, ms);
},
_setPageLoadTimeout = function(ms) {
_setTimeout(_const.TIMEOUT_NAMES.PAGE_LOAD, ms);
},
_aboutToDelete = function() {
@ -389,14 +475,21 @@ ghostdriver.Session = function(desiredCapabilities) {
getWindow : _getWindow,
closeWindow : _closeWindow,
getWindowsCount : _getWindowsCount,
initCurrentWindowIfNull : _initCurrentWindowIfNull,
getCurrentWindowHandle : _getCurrentWindowHandle,
getWindowHandles: _getWindowHandles,
isValidWindowHandle: _isValidWindowHandle,
getWindowHandles : _getWindowHandles,
isValidWindowHandle : _isValidWindowHandle,
aboutToDelete : _aboutToDelete,
setTimeout : _setTimeout,
getTimeout : _getTimeout,
timeoutNames : _timeoutNames,
inputs: _inputs
inputs : _inputs,
setScriptTimeout : _setScriptTimeout,
setAsyncScriptTimeout : _setAsyncScriptTimeout,
setImplicitTimeout : _setImplicitTimeout,
setPageLoadTimeout : _setPageLoadTimeout,
getScriptTimeout : _getScriptTimeout,
getAsyncScriptTimeout : _getAsyncScriptTimeout,
getImplicitTimeout : _getImplicitTimeout,
getPageLoadTimeout : _getPageLoadTimeout,
timeoutNames : _const.TIMEOUT_NAMES
};
};

View File

@ -207,7 +207,7 @@ ghostdriver.WebElementLocator = function(session) {
elementOrElements.hasOwnProperty("value")) {
// return if elements found OR we passed the "stopSearchByTime"
stopSearchByTime = searchStartTime + _session.getTimeout(_session.timeoutNames().IMPLICIT);
stopSearchByTime = searchStartTime + _session.getImplicitTimeout();
if (elementOrElements.value.length !== 0 || new Date().getTime() > stopSearchByTime) {
res.success(_session.getId(), elementOrElements.value);
return;
@ -215,7 +215,7 @@ ghostdriver.WebElementLocator = function(session) {
}
// retry if we haven't passed "stopSearchByTime"
stopSearchByTime = searchStartTime + _session.getTimeout(_session.timeoutNames().IMPLICIT);
stopSearchByTime = searchStartTime + _session.getImplicitTimeout();
if (stopSearchByTime >= new Date().getTime()) {
// Recursive call in 50ms
setTimeout(function(){