Variable not receiving value from javascript

I have the following execute javascript statement in my test:

WebUI.executeJavaScript('''
  var title_cells = document.querySelectorAll(".views-table tbody tr td.views-field-title");
  title_cells.forEach(function(cell) { 
    if (cell.innerHTML.includes("AUD Entitlement Product")) { 
      return cell.innerHTML; 
    } 
   })
''',  null)

This successfully returns the correct value. However, if I try to capture it in a variable using:

String audduration = WebUI.executeJavaScript('''
  var title_cells = document.querySelectorAll(".views-table tbody tr td.views-field-title");
  title_cells.forEach(function(cell) { 
    if (cell.innerHTML.includes("AUD Entitlement Product")) { 
      return cell.innerHTML; 
    } 
})
''', null)

the resulting value in “audduration” is null and doesn’t equal what the javascript is returning. So the "String audduration = " part of the statement is not working. Any ideas on what might be wrong?

Well, the code is assuming that the forEach loop is going to succeed. It may not. And if it doesn’t succeed, it will return undefined (which I believe may get translated to null in Groovy).

Background: When Groovy wraps your JS and passes it to the browser, it is inserted into an anonymous function like this:

WebUI.executeJavaScript('alert("hey!");', null)

// – becomes – //

function() {
  alert("hey!");
}();

And the result of a function that does not return anything, is undefined – which is exactly what that IIFE above would do.

So, question is, are you seeing the code returning undefined?

Oh wait… my bad…

You can’t exit early from forEach. You could use a traditional for loop or try Array.every / Array.some

I don’t know what " You can’t exit early from forEach . You could use a traditional for loop or try Array.every / Array.some" means, but I’ll ask my developer.

Regarding your first comment, the code returns the correct value, not “undefined”. But when it tries to store it in the variable, it fails to do so.

By “exit early” I’m referring to your return statement.

Show your “developer” this…

Understand this: undefined is what JavaScript returns from a function that returns no specified value. FACT. However, in Groovy, undefined has no meaning so it likely translates it to null which is a workable solution to the problem.

“undefined” is not being returned. We were able to output the value returned from the javascript and it is the correct value, which is “18”.

It is, Jeff, trust me. That’s how JavaScript works.

There is only one way I can think of for your forEach to be behaving differently: your “developer” has hijacked (overwritten) the Array.forEach prototype. Doubtful.

Try this. And before you run this code, be sure you understand what YOU are saying compared to what I am saying. This code backs up what I am saying but runs counter to what you are saying.

    String js = '''
      [1,2,3,4,5].forEach(function(n) {
        if(n === 3) {
          return n;  // WILL NOT WORK
        }
      });
    '''
    int n = WebUI.executeJavaScript(js, null)
    println "result: " + n

That should fail with an error complaining about casting null to an int – and rightly so. It’s broken code.

Now try this:

    String js = '''
      [1,2,3,4,5].forEach(function(n) {
        if(n === 3) {
          return n;  // WILL NOT WORK
        }
      });
      return 99;
    '''
    int n = WebUI.executeJavaScript(js, null)
    println "result: " + n

You’ll get 99 printed to the console. If you’ve understood what I was saying, it proves you cannot return a value from inside a genuine Array.forEach method. PERIOD.

Now, to answer your question (implied in the title of your post) - “Variable not receiving value from javascript”

  1. Your code as listed returns undefined.
  2. Groovy turns undefined into null
  3. You then assign null to audduration
  4. So your variable is receiving a value from JavaScript (just not the value you’re expecting because your code is invalid/not doing what you thought.)

After reviewing the code I initially submitted, it appears I pasted the wrong js. This is the code that is returning the correct value.

var title_cells = document.querySelectorAll(’.views-table tbody tr td.views-field-title’); title_cells.forEach(function(cell) {
if (cell.innerHTML.includes(‘AUD Entitlement Product’)) {
console.log(cell.nextElementSibling.innerHTML);
}
});
, null)

That code doesn’t return anything.

Yeah, because you’re not running it against the page on my website so there is no data for it to return. When run against the table shown in the attachment, it returns “2”, which is the correct value.

  1. I’m not running it anywhere, except in my head.
  2. That code does not return 2, it prints 2 to the console. Printing 2 and returning 2 a very different things.

Regardless, there is no return statement so it cannot return anything (except undefined when called from Groovy).

image

Jeff, I’m sorry, but if you are not going to listen to what I’m trying to teach you, I need to bow out of this cyclic (and frankly pretty pointless) conversation.

By way of courtesy, I’ll make a call out to a few other “code heads”, perhaps they can steer you straight:

@devalex88, @ThanhTo @Brandon_Hein @Marek_Melocik, @Andrej_Podhajsky

Good luck

I’m just the middle man between you and the developer I’m working with. I don’t know javascript nor do I understand it. I’m just relaying here. Also, I’m not trying to learn javascript right now, I’m just trying to get a value into a variable. Thanks.

I’m by no means a JS expert but I have a few suggestions that may help you narrow down the issue:

1.) Try something simple, like:

String test = WebUI.executeJavaScript("return 'Hello world!';", null);

If you get the appropriate value in ‘test’, then you know that it’s a problem with your JS, and not the WebUI method call.

2.) Try executing your JS directly through WebDriver, instead of the WebUI API:

WebDriver driver = DriverFactory.getWebDriver();
JavascriptExecutor executor = (JavascriptExecutor)driver;
String audduration = executor.executeScript("var title_cells = document.querySelectorAll('.views-table tbody tr td.views-field-title');
  title_cells.forEach(function(cell) { 
    if (cell.innerHTML.includes('AUD Entitlement Product')) { 
      return cell.innerHTML; 
    } 
})");

If you get the appropriate value returned here, then it’s either a bug in the WebUI.executeJavaScript() method, or an error in your usage of it.

If anyone is curious, here’s the WebUI.executeJavaScript() method implementation (which does basically what I’ve done in #2 from my previous post):

    @CompileStatic
    public Object executeJavascript(String script, List arguments, FailureHandling flowControl) {
        WebUIKeywordMain.runKeyword({
            WebDriver webDriver = getWebDriver()
            if (!(webDriver instanceof JavascriptExecutor)) {
                throw new StepFailedException(MessageFormat.format(CoreWebuiMessageConstants.KW_MSG_WEBDRIVER_DOES_NOT_SUPPORT_JS, webDriver.getClass().getName()))
            }
            JavascriptExecutor jsExecutor = (JavascriptExecutor) webDriver
            Object result = jsExecutor.executeScript(script, arguments != null ? arguments.toArray() : new Object[0])
            logger.logPassed(MessageFormat.format(CoreWebuiMessageConstants.KW_LOG_PASSED_EXECUTE_JS_SUCESSFULLY, script))
            return result
        }, flowControl, true, CoreWebuiMessageConstants.KW_MSG_UNABLE_TO_EXECUTE_JS)
    }

Valid code. Will return a string value “Hello world!”

But there’s no point, Brandon. His code does not return a value except undefined which is correct for that code when wrapped by Kat/Webdriver and inserted in the page. As I said earlier in the thread:

Jeff (and perhaps his developer?) think they can return early from .forEach, which, like .each in Groovy, means “visit them all” - there is no way to exit early.

1 Like

I think you’re probably right, just trying to address some unknowns. To your point, could he use a break; upon finding the correct element? Thinking something like:

var title_cells = document.querySelectorAll(".views-table tbody tr td.views-field-title");
var innerHTML;
title_cells.forEach(function(cell) { 
if (cell.innerHTML.includes("AUD Entitlement Product")) {
    innerHTML = cell.innerHTML;
    break;
} 
})
return innerHTML;

Nope. He needs the methods I showed him earlier:

This is probably the oldest/least-fashionable way to go about it…

  var title_cells = document.querySelectorAll('.views-table tbody tr td.views-field-title'); 
  for(var i = 0; i < title_cells.length; i++) {
    if (title_cells[i].innerHTML.includes('AUD Entitlement Product')) {
      return title_cells[i].nextElementSibling.innerHTML;
      //return title_cells[i].innerHTML;
    }
  }

Notice the two return statements - not sure which he needs.

But there’s no coverage for a completed loop there. Suggest returning -999 or something.