2

I am trying to locate an iframe by partial id. For this method, I used: driver.switchTo().frame(driver.findElement(By.cssSelector("iframe[id*='card-fields-number']"))); I have also tried xpath.

 driver.switchTo().frame(driver.findElement(By.xpath("//iframe[contains(@id,'card-fields-number')]")));

However, I still receive this exception:

org.openqa.selenium.NoSuchElementException: Returned node (null) was not a DOM element

I found out that when I enable javascript for HtmlUnit, it is able to locate the frame, and switch to it. I would rather not enable javascript as it runs very slow for me, and adds unneeded delay.

iFrame HTML code:

<iframe class="card-fields-iframe" frameborder="0" id="card-fields-number-7pbvqg7azsf00000" name="card-fields-number-7pbvqg7azsf00000" scrolling="no" src="https://checkout.shopifycs.com/number?identifier=438599641d0ed8fe61c161d72e62b5f8&amp;location=https%3A%2F%2Fshopnicekicks.com%2F2192362%2Fcheckouts%2F438599641d0ed8fe61c161d72e62b5f8&amp;dir=ltr&amp;fonts[]=Lato" title="Field container for: Card number" style="height: 43px;"></iframe>

iFrame ID is dynamic, so that is why I resort to using partial ID.

Website link: https://shopnicekicks.com/checkout You must fill everything out until you reach the last page, which is the credit card information page.

Update The iFrame is inside of the parent frame. Parent Frame:

<iframe srcdoc="<script>!function(){var e=function(e){var t={exports:{}};return e.call(t.exports,t,t.exports),t.exports},t=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,&quot;value&quot;in i&amp;&amp;(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&amp;&amp;e(t.prototype,n),i&amp;&amp;e(t,i),t}}(),n=function(e,t){if(!(e instanceof t))throw new TypeError(&quot;Cannot call a class as a function&quot;)},i=function(e){return e&amp;&amp;e.__esModule?e:{&quot;default&quot;:e}},o=e(function(e,i){&quot;use strict&quot;;Object.defineProperty(i,&quot;__esModule&quot;,{value:!0});var o=function(){function e(){var t=this;n(this,e),this.calls=[],window.ga=function(){for(var e=arguments.length,n=Array(e),i=0;i<e;i++)n[i]=arguments[i];return t.gaCall(n)}}return t(e,[{key:&quot;gaCall&quot;,value:function(e){var t=this;this.calls.push(e),clearTimeout(this.timeout),this.timeout=setTimeout(function(){t.calls.length>0&amp;&amp;t.sendMessage()},0)}},{key:&quot;listen&quot;,value:function(){var e=this;window.addEventListener(&quot;message&quot;,function(t){return e.receiveMessage(t)},!1)}},{key:&quot;sendMessage&quot;,value:function(){window.parent.postMessage({type:&quot;analytics&quot;,calls:this.calls},this.origin),this.calls=[]}},{key:&quot;receiveMessage&quot;,value:function(e){if(e.source===window.parent&amp;&amp;&quot;checkout_context&quot;===e.data.type){this.origin=e.origin,window.Shopify=e.data.Shopify,window.__st=e.data.__st;try{window.additionalScripts()}catch(e){console.error(&quot;User script error: &quot;,e)}}}}]),e}();i[&quot;default&quot;]=o});e(function(){&quot;use strict&quot;;var e=i(o);!function(){(new e[&quot;default&quot;]).listen()}()})}(&quot;undefined&quot;!=typeof global?global:&quot;undefined&quot;!=typeof window&amp;&amp;window); window.additionalScripts = function () {};</script>" src="https://checkout.shopify.com/2192362/sandbox/google_analytics_iframe" onload="this.setAttribute('data-loaded', true)" sandbox="allow-scripts" id="google-analytics-sandbox" tabindex="-1" class="visually-hidden" style="display:none" aria-hidden="true" data-loaded="true"></iframe>
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Jeff
  • 170
  • 2
  • 5
  • 19

3 Answers3

1

Just try with frame indexing "//iframe[index]", where index integer.

e.g. xpath : //iframe[1]

Frame id may change dynamically but in a few application structure remains same so indexing solves the problem.

Please let me know if it solves the issue.

Anuj Kapur
  • 11
  • 1
  • It finds an iframe, but is not able to identify the elements inside of it. – Jeff Feb 10 '19 at 15:23
  • Then only way is to find number of frames present as stated by sameer arora. if its a practice website that you are automation or publicly available website then please share the link. – Anuj Kapur Feb 10 '19 at 16:02
  • There are approximately 5 frames. However, it can only locate iframe[1]. The other frames print out `org.openqa.selenium.NoSuchFrameException: Unable to locate a node using //iframe[number]` – Jeff Feb 10 '19 at 17:41
1

As the iframe has tag iframe, you can switch to the iframe using the tagname like:
driver.switchTo().frame(driver.findElement(By.tagName("iframe")));
And if you want to again switch to the default content, then you can use driver.switchTo().defaultContent();

Sameer Arora
  • 4,439
  • 3
  • 10
  • 20
  • It switches to an iframe, and then fails to locate any element inside of the iframe. It might be because there are multiple iframes. – Jeff Feb 10 '19 at 15:15
  • Ok, so you need to find out how many frames are there and then you need to switch to the frames one by one and then you need to operate on the element. – Sameer Arora Feb 10 '19 at 15:19
1

This error message...

org.openqa.selenium.NoSuchElementException: Returned node (null) was not a DOM element

...implies that there was no such element found as the returned node was null or was not a DOM element.

This is still a open issue with htmlunit-driver team.

However there are certain things which you need to take care as follows:

  • First and foremost, all the modern browsers come with built-in support for JavaScript.
  • HtmlUnitDriver is a WebDriver compatible driver for HtmlUnit headless browser. It has fairly good JavaScript support (which is constantly improving) and is able to work even with quite complex AJAX libraries, simulating Chrome, Firefox or Internet Explorer depending on the configuration used. So ideally while working with HtmlUnitDriver, JavaScript must be enabled, else HtmlUnitDriver may not ne able to detect the JavaScript based elements.
  • The element seems to be a credit card field and historically Credit Card Number, etc resides within <iframes>.
  • Whenever an <iframe> is in play src attribute of the <iframe> tag plays a vital role.
  • As per best practices while switching <iframe> you need to induce WebDriverWait for the desired frame to be available and switch to it.

So you can use either of the following solutions:

  • cssSelector:

    new WebDriverWait(driver, 10).until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.cssSelector("iframe.card-fields-iframe[id^='card-fields-number-'][src*='shopifycs']")));
    
  • xpath:

    new WebDriverWait(driver, 10).until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.xpath("//iframe[@class='card-fields-iframe' and starts-with(@id,'card-fields-number-')][contains(@src, 'shopifycs')]")));
    

Update

See the snapshot of the CssSelector which identifies the element perfecto as per the HTML you have provided:

CssSelector

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • Both CssSelector and xpath return this exception. `org.openqa.selenium.TimeoutException: Expected condition failed: waiting for frame to be available:` – Jeff Feb 10 '19 at 15:13
  • @Jeff Check out the update to my answer and let me know your thoughts about it. – undetected Selenium Feb 10 '19 at 15:59
  • Thank you for answering. However, you said that javascript must be enabled, but I was wondering if there was a way to do it without javascript being enabled. It works perfectly like you said when it is enabled. – Jeff Feb 10 '19 at 17:31
  • @Jeff I have already provided the link to the related discussion about `HtmlUnitDriver does not load javascript when navigating a page from an url` – undetected Selenium Feb 10 '19 at 18:11