Selenium thorws "unkown locator" error when Javascript executor should return a web element

1,355 views
Skip to first unread message

Alan Treviño

unread,
Feb 4, 2016, 4:30:10 PM2/4/16
to webdriver
Problem:
I've already created a javascript that finds element by cssLocator using "QuerySelector" method but returning this element through selenium, I get "unkown locator" error
starting to think the problem is the way the app is built because if I open app's html code, the element is not displayed in that code unless you inspect using F12 and selecting the element.

Element description:
A radio button that has the same id, name, class than other radio button, so i'm trying to find it using cssLocator.

HTML:
<div id="msrp_edit_div" style="display: inline;">
 
<input onclick="notRelavntScript()" name="msrp_compliance" checked="" id="msrp_compliance" value="t" type="radio"> Yes
 
<input onclick="notRelavntScript()" name="msrp_compliance" id="msrp_compliance" value="f" type="radio"> No
</div>


The javascript:
/* This works for all kind of css locators,* @return WebElement object*/
function findElementByQuerySelector(cssSelector) {
 
var frames = document.getElementsByTagName('iframe');
 
var element = null;
 
/*Iterate through all of the iframes until we found a match for the input*/
 
for (var f = 0; f < frames.length; f++) {
 
var innerDoc = frames[f].contentDocument || iframes[f].contentWindow.document;
 
var selector = innerDoc.querySelector(cssSelector);
 
if (selector != 'undefined') {
 
return selector;
 
}
 
}
}

Java/Selenium code:
public String executeScript(String jsString){
 
//Setup method variables
 session
= WebdriverSession.getInstance();
 
JavascriptExecutor js = (JavascriptExecutor) session.getDriver();
 
String result = "null";
 
 
//Attempt to execute the javascript search function
 
try{
 result
= js.executeScript(jsString).toString();
 
}
 
catch(Exception e){
 e
.getMessage();
 
}
 
return result;
}

This selenium code executes the javascript so it will be something like:
result = js.executeScript("/* This works for all kind of css locators,* @return WebElement object*/ function findElementByQuerySelector(cssSelector) { var frames = document.getElementsByTagName('iframe'); var element = null; /*Iterate through all of the iframes until we found a match for the input*/ for (var f = 0; f < frames.length; f++) { var innerDoc = frames[f].contentDocument || iframes[f].contentWindow.document; var selector = innerDoc.querySelector(cssSelector); if (selector != 'undefined') { return selector; } } } var result = findElementByQuerySelector('input[value=t]'); return result;").toString();

CssLocator used to find one of the radio buttons:
css=input[value=t]

The error sent by selenium:
[org.openqa.selenium.remote.RemoteWebElement@38 -> unknown locator]

So If I execute this code directly on Mozilla Firefox console, it returns:
<input onclick="notRelavntScript()" name="msrp_compliance" checked="" id="msrp_compliance" value="t" type="radio"> Yes

This is the WebElement I think selenium should understand and parse to string.
I use similar codes to find different elements but using elementFound.id or elementFound.name  the difference here is that i return only the elementFound.

Will be glad to get some help, and any comments are welcome. Thanks.

David

unread,
Feb 5, 2016, 3:24:04 PM2/5/16
to webdriver
Curious though, why are you finding elements with JS in Selenium? Selenium has it's own find element methods. Is it for code reuse since you use these JS methods elsewhere?

One would think that what works in a browser's javascript/developer/error console should work in Selenium's JS executor. Maybe the iframes thing is giving issues with Selenium, as I recall there's some special cases for dealing with frames and iframes in Selenium (as in you having to switch to frame to then find their elements).

If you're not required to use JS to find elements, I'd try the native Selenium method to find and iterate through the iframes and find the element within the iframe.

Also, why are you returning the element on Java side as a string? For debugging purposes? I would think you'd want to do this:

WebElement result = (WebElement) js.executeScript(jsString); 
 //now do whatever with the result WebElement object

Mark Collin

unread,
Feb 6, 2016, 3:08:39 AM2/6/16
to webdriver
You will have mixed luck using css selectors that try and match based upon the value of an attribute.  Try using an XPath instead for things like this, e.g.:

driver.findElement(By.xpath("//div[@id='msrp_edit_div']/input[@value='t']"))

To reiterate what David said, don't reach for the JavascriptExecutor until you really need to. You shouldn't need to use it unless you come across some quite complicated things you want to test.

Alan Treviño

unread,
Feb 24, 2016, 12:16:40 AM2/24/16
to webdriver
Sorry for the late response.

For your first question "Is it for code reuse since you use these JS methods elsewhere?" the answer is Yes, I pretend to reuse this JS method along my tests every time I look for specific radio buttons...I have others cases where I found labels, inputs, parent inputs, etc. etc. the framework is supported in a 90% of JS, why? because it's a very dynamical framework where testers look for Username label in DOM and it has a relationship with an text input so I get the id and don't let testers to inspect the application is being tested. Sorry if I don't explained myself...

Secondly, you said something that really surprised me, "One would think that what works in a browser's javascript/developer[...]"...
As far as I understand, javascript can be run from console (i.e. Google's dev tools console) and selenium interface JavascriptExecutor, I think the only command cannot be run from the interface are those specifically created to support the console.

Reading docs:

it says: Because of cross domain policies browsers enforce your script execution may fail unexpectedly and without adequate error messaging.
So if we don't care too much about this, my next question is Why should I care about using selenium's native methods?

Correct me if I'm wrong (I don't have the code at the time of writing this post), but ...Selenium native mathods like findElementByCssSelector,findElementByXpath or any other like these, aren't they supposed to be a javascript call at the end of the day? if this is correct, what's the problem of calling JSExecutor.

And lastly... about returning the string, that was because I was debugging,but normally I convert it to string because i get id, name or any other value as string not as a WebElement or any other data type.

David

unread,
Feb 24, 2016, 1:00:04 PM2/24/16
to webdriver
I hope you read my other point in the comment about iframes. In a browser console, you may have more freedom to deal with frames, iframes, etc. although if I'm not mistaken, I recall you may still sometimes have to switch to specific frame/iframe to then manipulate (or query for) the DOM elements of that iframe. But it's easier to switch since on Google Chrome at least, there was like a frame selector menu dropdown to select from in the console UI. And when debugging/testing with console, you don't think too much about these things sometimes.

From the Selenium perspective, you don't have direct access to all elements on page if some are within frames, iframes. Just like with windows and popups, you have to switch to them before you can deal with the elements within them. That might be one possibility where you are having problems running the JS code in Selenium.

You could alternatively try this approach:
  1. In Selenium code, do a loop iterating over iframes (e.g. a loop to switch through all iframes on the page). See this post for examples of switching frames: http://www.seleniumeasy.com/selenium-tutorials/how-to-work-with-iframes-in-selenium-webdriver. You could switch by index, or first find all iframe WebElements and then iterating through each such WebElement passing it to the switchTo method call.
  2. Within the loop where after you've switched to the given iframe, now call your JS code to find your querySelector/element, and return element if found, else continue with loop. If no elements found during loop, you could return null, etc. So this does mean you have to rework the JS code as you no longer need to get all iframes then iterate over them in the JS code because that part is now done in Selenium. So you only need to find the elements from the context of the current iframe (assuming you've switched to it in Selenium).
Why do it this way? I'm guessing executing through Selenium, the DOM model or the security model could be different in that you can't just directly grab all iframes and extract elements inside them, even if done via JS, you have to explicitly say you want a given iframe (switching to it) then you can access it's element content inside. There's no way of "switching" iframes from within JS from the Selenium perspective so you will have to use the native Selenium APIs there. See if this alternate approach works for you or not.
Reply all
Reply to author
Forward
0 new messages