mirror of
https://github.com/teampoltergeist/poltergeist.git
synced 2022-11-09 12:05:00 -05:00
Merge pull request #911 from teampoltergeist/evaluate_async_script
Add Driver#evaluate_async_script
This commit is contained in:
commit
7b336bfb4e
10 changed files with 175 additions and 3 deletions
|
@ -11,6 +11,7 @@ module Capybara::Poltergeist
|
|||
'Poltergeist.InvalidSelector' => InvalidSelector,
|
||||
'Poltergeist.StatusFailError' => StatusFailError,
|
||||
'Poltergeist.NoSuchWindowError' => NoSuchWindowError,
|
||||
'Poltergeist.ScriptTimeoutError' => ScriptTimeoutError,
|
||||
'Poltergeist.UnsupportedFeature' => UnsupportedFeature,
|
||||
'Poltergeist.KeyError' => KeyError,
|
||||
}
|
||||
|
@ -125,6 +126,10 @@ module Capybara::Poltergeist
|
|||
command 'evaluate', script, *args
|
||||
end
|
||||
|
||||
def evaluate_async(script, wait_time, *args)
|
||||
command 'evaluate_async', script, wait_time, *args
|
||||
end
|
||||
|
||||
def execute(script, *args)
|
||||
command 'execute', script, *args
|
||||
end
|
||||
|
|
|
@ -208,6 +208,18 @@ class Poltergeist.Browser
|
|||
throw new Poltergeist.ObsoleteNode if arg["ELEMENT"]["page_id"] != @currentPage.id
|
||||
@current_command.sendResponse @currentPage.evaluate("function() { return #{script} }", args...)
|
||||
|
||||
evaluate_async: (script, max_wait, args...) ->
|
||||
for arg in args when @_isElementArgument(arg)
|
||||
throw new Poltergeist.ObsoleteNode if arg["ELEMENT"]["page_id"] != @currentPage.id
|
||||
command = @current_command
|
||||
cb = (result)=>
|
||||
command.sendResponse(result)
|
||||
@currentPage.evaluate_async("function() { #{script} }", cb, args...)
|
||||
setTimeout(=>
|
||||
command.sendError(new Poltergeist.ScriptTimeoutError)
|
||||
, max_wait*1000)
|
||||
|
||||
|
||||
execute: (script, args...) ->
|
||||
for arg in args when @_isElementArgument(arg)
|
||||
throw new Poltergeist.ObsoleteNode if arg["ELEMENT"]["page_id"] != @currentPage.id
|
||||
|
|
|
@ -266,6 +266,31 @@ Poltergeist.Browser = (function() {
|
|||
return this.current_command.sendResponse((ref = this.currentPage).evaluate.apply(ref, ["function() { return " + script + " }"].concat(slice.call(args))));
|
||||
};
|
||||
|
||||
Browser.prototype.evaluate_async = function() {
|
||||
var arg, args, cb, command, i, len, max_wait, ref, script;
|
||||
script = arguments[0], max_wait = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
for (i = 0, len = args.length; i < len; i++) {
|
||||
arg = args[i];
|
||||
if (this._isElementArgument(arg)) {
|
||||
if (arg["ELEMENT"]["page_id"] !== this.currentPage.id) {
|
||||
throw new Poltergeist.ObsoleteNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
command = this.current_command;
|
||||
cb = (function(_this) {
|
||||
return function(result) {
|
||||
return command.sendResponse(result);
|
||||
};
|
||||
})(this);
|
||||
(ref = this.currentPage).evaluate_async.apply(ref, ["function() { " + script + " }", cb].concat(slice.call(args)));
|
||||
return setTimeout((function(_this) {
|
||||
return function() {
|
||||
return command.sendError(new Poltergeist.ScriptTimeoutError);
|
||||
};
|
||||
})(this), max_wait * 1000);
|
||||
};
|
||||
|
||||
Browser.prototype.execute = function() {
|
||||
var arg, args, i, len, ref, script;
|
||||
script = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
|
|
|
@ -214,6 +214,23 @@ Poltergeist.NoSuchWindowError = (function(superClass) {
|
|||
|
||||
})(Poltergeist.Error);
|
||||
|
||||
Poltergeist.ScriptTimeoutError = (function(superClass) {
|
||||
extend(ScriptTimeoutError, superClass);
|
||||
|
||||
function ScriptTimeoutError() {
|
||||
return ScriptTimeoutError.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
ScriptTimeoutError.prototype.name = "Poltergeist.ScriptTimeoutError";
|
||||
|
||||
ScriptTimeoutError.prototype.args = function() {
|
||||
return [];
|
||||
};
|
||||
|
||||
return ScriptTimeoutError;
|
||||
|
||||
})(Poltergeist.Error);
|
||||
|
||||
Poltergeist.UnsupportedFeature = (function(superClass) {
|
||||
extend(UnsupportedFeature, superClass);
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
var slice = [].slice,
|
||||
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
|
||||
slice = [].slice,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
hasProp = {}.hasOwnProperty;
|
||||
|
||||
Poltergeist.WebPage = (function() {
|
||||
var command, delegate, fn1, fn2, i, j, len, len1, ref, ref1;
|
||||
|
||||
WebPage.CALLBACKS = ['onConsoleMessage', 'onError', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested', 'onResourceReceived', 'onResourceError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated', 'onClosing'];
|
||||
WebPage.CALLBACKS = ['onConsoleMessage', 'onError', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested', 'onResourceReceived', 'onResourceError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated', 'onClosing', 'onCallback'];
|
||||
|
||||
WebPage.DELEGATES = ['open', 'sendEvent', 'uploadFile', 'render', 'close', 'renderBase64', 'goBack', 'goForward', 'reload'];
|
||||
|
||||
|
@ -16,6 +17,7 @@ Poltergeist.WebPage = (function() {
|
|||
function WebPage(_native) {
|
||||
var callback, i, len, ref;
|
||||
this._native = _native;
|
||||
this._checkForAsyncResult = bind(this._checkForAsyncResult, this);
|
||||
this._native || (this._native = require('webpage').create());
|
||||
this.id = 0;
|
||||
this.source = null;
|
||||
|
@ -30,6 +32,8 @@ Poltergeist.WebPage = (function() {
|
|||
this._requestedResources = {};
|
||||
this._responseHeaders = [];
|
||||
this._tempHeadersToRemoveOnRedirect = {};
|
||||
this._asyncResults = {};
|
||||
this._asyncEvaluationId = 0;
|
||||
ref = WebPage.CALLBACKS;
|
||||
for (i = 0, len = ref.length; i < len; i++) {
|
||||
callback = ref[i];
|
||||
|
@ -119,6 +123,11 @@ Poltergeist.WebPage = (function() {
|
|||
return true;
|
||||
};
|
||||
|
||||
WebPage.prototype.onCallbackNative = function(data) {
|
||||
this._asyncResults[data['command_id']] = data['command_result'];
|
||||
return true;
|
||||
};
|
||||
|
||||
WebPage.prototype.onResourceRequestedNative = function(request, net) {
|
||||
var ref2;
|
||||
this._networkTraffic[request.id] = {
|
||||
|
@ -568,6 +577,20 @@ Poltergeist.WebPage = (function() {
|
|||
return result;
|
||||
};
|
||||
|
||||
WebPage.prototype.evaluate_async = function() {
|
||||
var args, callback, cb, command_id, fn, ref2;
|
||||
fn = arguments[0], callback = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
command_id = ++this._asyncEvaluationId;
|
||||
cb = callback;
|
||||
this.injectAgent();
|
||||
(ref2 = this["native"]()).evaluate.apply(ref2, ["function(){ var page_id = arguments[0]; var args = []; for(var i=1; i < arguments.length; i++){ if ((typeof(arguments[i]) == 'object') && (typeof(arguments[i]['ELEMENT']) == 'object')){ args.push(window.__poltergeist.get(arguments[i]['ELEMENT']['id']).element); } else { args.push(arguments[i]) } } args.push(function(result){ result = window.__poltergeist.wrapResults(result, page_id); window.callPhantom( { command_id: " + command_id + ", command_result: result } ); }); " + (this.stringifyCall(fn, "args")) + "; return}", this.id].concat(slice.call(args)));
|
||||
setTimeout((function(_this) {
|
||||
return function() {
|
||||
return _this._checkForAsyncResult(command_id, cb);
|
||||
};
|
||||
})(this), 10);
|
||||
};
|
||||
|
||||
WebPage.prototype.execute = function() {
|
||||
var args, fn, ref2;
|
||||
fn = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
|
@ -643,6 +666,19 @@ Poltergeist.WebPage = (function() {
|
|||
}
|
||||
};
|
||||
|
||||
WebPage.prototype._checkForAsyncResult = function(command_id, callback) {
|
||||
if (this._asyncResults.hasOwnProperty(command_id)) {
|
||||
callback(this._asyncResults[command_id]);
|
||||
delete this._asyncResults[command_id];
|
||||
} else {
|
||||
setTimeout((function(_this) {
|
||||
return function() {
|
||||
return _this._checkForAsyncResult(command_id, callback);
|
||||
};
|
||||
})(this), 50);
|
||||
}
|
||||
};
|
||||
|
||||
WebPage.prototype._blockRequest = function(url) {
|
||||
var blacklisted, useWhitelist, whitelisted;
|
||||
useWhitelist = this.urlWhitelist.length > 0;
|
||||
|
|
|
@ -74,6 +74,10 @@ class Poltergeist.NoSuchWindowError extends Poltergeist.Error
|
|||
name: "Poltergeist.NoSuchWindowError"
|
||||
args: -> []
|
||||
|
||||
class Poltergeist.ScriptTimeoutError extends Poltergeist.Error
|
||||
name: "Poltergeist.ScriptTimeoutError"
|
||||
args: -> []
|
||||
|
||||
class Poltergeist.UnsupportedFeature extends Poltergeist.Error
|
||||
constructor: (@message) ->
|
||||
name: "Poltergeist.UnsupportedFeature"
|
||||
|
|
|
@ -3,7 +3,7 @@ class Poltergeist.WebPage
|
|||
'onLoadFinished', 'onInitialized', 'onLoadStarted',
|
||||
'onResourceRequested', 'onResourceReceived', 'onResourceError',
|
||||
'onNavigationRequested', 'onUrlChanged', 'onPageCreated',
|
||||
'onClosing']
|
||||
'onClosing', 'onCallback']
|
||||
|
||||
@DELEGATES = ['open', 'sendEvent', 'uploadFile', 'render', 'close',
|
||||
'renderBase64', 'goBack', 'goForward', 'reload']
|
||||
|
@ -29,6 +29,8 @@ class Poltergeist.WebPage
|
|||
@_requestedResources = {}
|
||||
@_responseHeaders = []
|
||||
@_tempHeadersToRemoveOnRedirect = {}
|
||||
@_asyncResults = {}
|
||||
@_asyncEvaluationId = 0
|
||||
|
||||
for callback in WebPage.CALLBACKS
|
||||
this.bindCallback(callback)
|
||||
|
@ -85,6 +87,10 @@ class Poltergeist.WebPage
|
|||
@errors.push(message: message, stack: stackString)
|
||||
return true
|
||||
|
||||
onCallbackNative: (data) ->
|
||||
@_asyncResults[data['command_id']] = data['command_result']
|
||||
true
|
||||
|
||||
onResourceRequestedNative: (request, net) ->
|
||||
@_networkTraffic[request.id] = {
|
||||
request: request,
|
||||
|
@ -369,6 +375,32 @@ class Poltergeist.WebPage
|
|||
return window.__poltergeist.wrapResults(_result, page_id); }", @id, args...)
|
||||
result
|
||||
|
||||
evaluate_async: (fn, callback, args...) ->
|
||||
command_id = ++@_asyncEvaluationId
|
||||
cb = callback
|
||||
this.injectAgent()
|
||||
this.native().evaluate("function(){
|
||||
var page_id = arguments[0];
|
||||
var args = [];
|
||||
for(var i=1; i < arguments.length; i++){
|
||||
if ((typeof(arguments[i]) == 'object') && (typeof(arguments[i]['ELEMENT']) == 'object')){
|
||||
args.push(window.__poltergeist.get(arguments[i]['ELEMENT']['id']).element);
|
||||
} else {
|
||||
args.push(arguments[i])
|
||||
}
|
||||
}
|
||||
args.push(function(result){
|
||||
result = window.__poltergeist.wrapResults(result, page_id);
|
||||
window.callPhantom( { command_id: #{command_id}, command_result: result } );
|
||||
});
|
||||
#{this.stringifyCall(fn, "args")};
|
||||
return}", @id, args...)
|
||||
|
||||
setTimeout( =>
|
||||
@_checkForAsyncResult(command_id, cb)
|
||||
, 10)
|
||||
return
|
||||
|
||||
execute: (fn, args...) ->
|
||||
this.native().evaluate("function() {
|
||||
for(var i=0; i < arguments.length; i++){
|
||||
|
@ -426,6 +458,16 @@ class Poltergeist.WebPage
|
|||
else
|
||||
throw new Poltergeist.UnsupportedFeature("clearMemoryCache is supported since PhantomJS 2.0.0")
|
||||
|
||||
_checkForAsyncResult: (command_id, callback)=>
|
||||
if @_asyncResults.hasOwnProperty(command_id)
|
||||
callback(@_asyncResults[command_id])
|
||||
delete @_asyncResults[command_id]
|
||||
else
|
||||
setTimeout(=>
|
||||
@_checkForAsyncResult(command_id, callback)
|
||||
, 50)
|
||||
return
|
||||
|
||||
_blockRequest: (url) ->
|
||||
useWhitelist = @urlWhitelist.length > 0
|
||||
|
||||
|
|
|
@ -139,6 +139,11 @@ module Capybara::Poltergeist
|
|||
unwrap_script_result(result)
|
||||
end
|
||||
|
||||
def evaluate_async_script(script, *args)
|
||||
result = browser.evaluate_async(script, session_wait_time, *args.map { |arg| arg.is_a?(Capybara::Poltergeist::Node) ? arg.native : arg})
|
||||
unwrap_script_result(result)
|
||||
end
|
||||
|
||||
def execute_script(script, *args)
|
||||
browser.execute(script, *args.map { |arg| arg.is_a?(Capybara::Poltergeist::Node) ? arg.native : arg})
|
||||
nil
|
||||
|
|
|
@ -174,6 +174,13 @@ module Capybara
|
|||
end
|
||||
end
|
||||
|
||||
class ScriptTimeoutError < Error
|
||||
def message
|
||||
"Timed out waiting for evaluated script to resturn a value"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class DeadClient < Error
|
||||
def initialize(message)
|
||||
@message = message
|
||||
|
|
|
@ -1424,5 +1424,24 @@ module Capybara::Poltergeist
|
|||
})
|
||||
end
|
||||
end
|
||||
|
||||
context 'evaluate_async_script' do
|
||||
it 'handles evaluate_async_script value properly' do
|
||||
@session.using_wait_time(5) do
|
||||
expect(@session.driver.evaluate_async_script('arguments[0](null)')).to be_nil
|
||||
expect(@session.driver.evaluate_async_script('arguments[0](false)')).to be false
|
||||
expect(@session.driver.evaluate_async_script('arguments[0](true)')).to be true
|
||||
expect(@session.driver.evaluate_async_script("arguments[0]({foo: 'bar'})")).to eq({'foo' => 'bar'})
|
||||
end
|
||||
end
|
||||
|
||||
it 'will timeout' do
|
||||
@session.using_wait_time(1) do
|
||||
expect {
|
||||
@session.driver.evaluate_async_script('var callback=arguments[0]; setTimeout(function(){callback(true)}, 4000)')
|
||||
}.to raise_error Capybara::Poltergeist::ScriptTimeoutError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue