1
0
Fork 0
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:
Thomas Walpole 2017-11-02 10:33:24 -07:00 committed by GitHub
commit 7b336bfb4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 175 additions and 3 deletions

View file

@ -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

View file

@ -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

View file

@ -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) : [];

View file

@ -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);

View file

@ -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;

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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