Reputation: 5580
import webdriver from 'selenium-webdriver';
const driver = new webdriver.Builder()
.withCapabilities(webdriver.Capabilities.chrome())
.build();
driver.get('https://www.google.com');
let foo = function(rules) {
rules.forEach(rule => {
rule();
});
}
let bar = function() { return 'bar' };
let baz = function() { return 'baz' };
driver.executeScript(foo, [bar, baz]).then(function(result) {
console.log(result);
});
driver.quit();
it errors out with
WebDriverError: unknown error: rule is not a function
let foo = function(rules) {
return rules;
// rules.forEach(rule => {
// rule();
// });
}
let bar = function() { return 'bar' };
let baz = function() { return 'baz' };
driver.executeScript(foo, [bar, baz]).then(function(result) {
console.log(result); // refer the log pasted below
});
Looks like the function are being serialized a string
[ 'function bar() {\n return \'bar\';\n}',
'function baz() {\n return \'baz\';\n}' ]
Any pointer on how to pass array of functions as arguments
would be helpful.
Upvotes: 1
Views: 3096
Reputation: 5580
I have found a different work around, w/o using eval
explicitly, but in similar fashion.
inject the function.toString()
as the content of a <script>
function foo(rules) {
var result = [];
rules.forEach(rule => {
result.push(rule());
});
return result;
}
function bar() { return 'bar' };
function baz() { return 'baz' };
function inject(content) {
var script = document.createElement('script');
script.innerHTML = content;
document.head.appendChild(script);
}
let script = `${bar.toString()} ${baz.toString()} ${foo.toString()}`;
driver.executeScript(inject, script);
then execute the desired function as
driver.executeScript('return foo([bar, baz])').then(function(result) {
// use the result
});
complete example
// example.js
import webdriver from 'selenium-webdriver';
const driver = new webdriver.Builder()
.withCapabilities(webdriver.Capabilities.chrome())
.build();
driver.get('https://www.google.com');
function foo(rules) {
var result = [];
rules.forEach(rule => {
result.push(rule());
});
return result;
}
function bar() { return 'bar' };
function baz() { return 'baz' };
function inject(content) {
var script = document.createElement('script');
script.innerHTML = content;
document.head.appendChild(script);
}
let script = `${bar.toString()} ${baz.toString()} ${foo.toString()}`;
driver.executeScript(inject, script);
driver.executeScript('return foo([bar, baz])').then(function(result) {
console.log(result);
});
driver.quit();
> babel-node example.js
[ 'bar', 'baz' ]
Upvotes: 2
Reputation: 5137
I think you can use eval. See code below:
driver.get('https://www.google.com');
let foo = function(rules) {
var results = [];
rules.forEach(rule => {
results.push(eval(rule));
});
return results;
}
let bar = "(function() { return ' message returned from bar' })()";
let baz = "(function() { return 'message returned from baz' })()";
driver.executeScript(foo, [bar, baz]).then(function(result) {
console.log(result);
});
Upvotes: 0
Reputation: 3702
Problem: All non trivial data passed as arguments to function are converted to string because it's the only way to inject something from selenium driver to browser.
Arguments must be a number, a boolean, a String, WebElement, or a List of any combination of the above.
Solution
You can convert string to function and execute it using eval
or function constructor new Function('...function body here...')
. Yes, it's a very bad, but actually, there is no other way to pass non trivial data from driver to browser. Actually, when you call this driver.executeScript(foo, [], ...)
, foo
function is also converted to string and executed in browser using eval
too.
If I were you, I would try to to find another way to achieve results without passing functions as arguments.
You can read more at: https://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/JavascriptExecutor.html
Upvotes: 0