Sarbbottam
Sarbbottam

Reputation: 5580

selenium-webdriver pass an array of functions as argument to executeScript

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

Answers (3)

Sarbbottam
Sarbbottam

Reputation: 5580

I have found a different work around, w/o using eval explicitly, but in similar fashion.

  1. the functions to be injected must be named function
  2. 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);
    
  3. 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

Buaban
Buaban

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

Timur Bilalov
Timur Bilalov

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

Related Questions