0

So in helping another user troubleshoot a Selenium/selector issue, I believe I ran in to a larger issue for which I'm wondering if there is any solution.

Basically, I have come upon (i)frame hell. Sorry to include a screenshot of the dev console, but I couldn't find a good way to copy it over.

too many frames

So, as you can see if you will kindly click the above image, the HTML for this page features frames inside framesets inside frames inside....

I was able to identify the first level of frames, but of course the element we wanted to locate was in the middle of all these frames.

What I found is that using xpath, cssSelector, ID, Name, any type of locator, I could never find any element past and including anything nested beneath a #document element. I've read on quite a few sites that this is either very difficult or impossible to do, but I never came across a solution that would work for this page.

Is this possible, and if so how can one get past these #document elements?

P.S. let me know if there is an easy way to copy the actual text as pictured from the console and I will do so for the whole page.

This is not a duplicate of that question because this is regarding multiple nested Frames with #document elements that Selenium apparently cannot get past. All examples provided only have one (i)Frame. I know how to switch to iFrame, but it seems is impossible to switch to an element or frame inside nested Frames that contain #document using Selenium alone; jQuery is a solution to this problem so I accepted the below answer.

I really don't care if it erroneously gets closed as a duplicate since I have gotten a solution but I am 100% certain it is a separate issue from simply switching to an iFrame.

C. Peck
  • 3,641
  • 3
  • 19
  • 36
  • Possible duplicate of [Find elements inside forms and iframe using Java and Selenium WebDriver](https://stackoverflow.com/questions/24247490/find-elements-inside-forms-and-iframe-using-java-and-selenium-webdriver) – JeffC Mar 13 '19 at 19:52
  • Not a duplicate (of that post at least), see explanation I added at the bottom of my question. – C. Peck Mar 13 '19 at 20:29
  • It is a dup... you just need to switch into multiple frames. Here are some questions that use the same approach and it worked. It has nothing to do with #document because as you can see from these posts, they have the same HTML format. [this](https://stackoverflow.com/questions/38363643/python-selenium-get-inside-a-document), [this](https://stackoverflow.com/questions/21474605/what-does-document-mean), [this](https://stackoverflow.com/questions/24360135/python-selenium-webdriver-finding-document-element) – JeffC Mar 13 '19 at 21:19
  • You need to add the code you are actually using and post the relevant HTML in the question if you want us to be able you with things like this. We can eliminate obvious questions, etc. – JeffC Mar 13 '19 at 21:20
  • The three examples you provide are regarding a **single** (i)frame which is easy to switch to and a different format than my example which features **nested** frames. This question is about whether Selenium can find additional frames inside a frame's `#document`, and the answer seems to be Selenium simply can't do that because even after switching to the first frame (successfully). If you know that to be wrong please correct me. – C. Peck Mar 13 '19 at 22:18
  • As far as posting code, I have not written any as I myself am not testing this website. I just spent a large amount of time trying to find a way to select an element when helping another user, and after researching found that I cannot switch to the inner nested frames because they need to be identified by a selector, and the selector in Selenium simply doesn't seem to look inside the `#document` element. Once again, I really don't care if it erroneously gets closed as a duplicate since I got a solution but I am 100% certain it is a separate issue from simply switching to an iFrame. – C. Peck Mar 13 '19 at 22:22
  • Actually I noticed the second example in your above comment has two frames, but not one nested in a #document element. – C. Peck Mar 13 '19 at 22:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/189981/discussion-between-c-peck-and-jeffc). – C. Peck Mar 13 '19 at 22:45
  • It doesn't change just because they are nested. You switch into the outer frame, locate the next frame and switch into it. You keep doing that until you get to the right frame and then you can access your element. – JeffC Mar 14 '19 at 01:58
  • That was one of the first thing I tried and it doesn't work for me. – C. Peck Mar 14 '19 at 02:00

1 Answers1

1

So this is ultimately the perfect use case for an extension I made. Here is the most important part of it:

  /// <summary>
        /// Selenium sometimes has trouble finding elements on the page. Give it some help by using JQuery to grab the full qualified xpath to that element.
        /// </summary>
        /// <param name="cssSelector"></param>
        /// <returns></returns>
        public static string GetFullyQualifiedXPathToElement(string cssSelector, bool isFullJQuery = false, bool noWarn = false)
        {

            if (cssSelector.Contains("$(") && !isFullJQuery) {

                isFullJQuery = true;

            }
            string finder_method = @"
                        function getPathTo(element) {
                            if(typeof element == 'undefined') return '';
                            if (element.tagName == 'HTML')
                                return '/HTML[1]';
                            if (element===document.body)
                                return '/HTML[1]/BODY[1]';

                            var ix= 0;
                            var siblings = element.parentNode.childNodes;
                            for (var i= 0; i< siblings.length; i++) {
                                var sibling= siblings[i];
                                if (sibling===element)
                                    return getPathTo(element.parentNode)+'/'+element.tagName+'['+(ix+1)+']';
                                if (sibling.nodeType===1 && sibling.tagName===element.tagName)
                                    ix++;
                            }
                        }
            ";
            if(isFullJQuery) {

                cssSelector = cssSelector.TrimEnd(';');

            }
            string executable = isFullJQuery ? string.Format("{0} return getPathTo({1}[0]);", finder_method, cssSelector) : string.Format("{0} return getPathTo($('{1}')[0]);", finder_method, cssSelector.Replace("'", "\""));
            string xpath = string.Empty;
            try {

                xpath = BaseTest.Driver.ExecuteJavaScript<string>(executable);

            } catch (Exception e) {

                if (!noWarn)  {

                    Check.Warn(string.Format("Exception occurred while building a dynamic Xpath. Css selector supplied to locate element is \"{0}\". Exception [{1}].", cssSelector, e.Message));

                }

            }
            if (!noWarn && string.IsNullOrEmpty(xpath)) {

                Check.Warn(string.Format("Supplied cssSelector did not point to an element. Selector is \"{0}\".", cssSelector));

            }
            return xpath;

        }

With this logic, you can pass a Jquery selector into your browser via javascript executor. JQuery has no problems finding elements nested within iframes. Try something like this:

driver.FindElement(By.XPath(GetFullyQualifiedXPathToElement("#MyDeeplyNestedElement")).Click();

https://gist.github.com/tsibiski/04410e9646ee9ced9f3794266d6c5a82

Feel free to remove whatever is in that method/class that does not apply to your situation.

Why/How does this suddenly make an element findable to Selenium????

You may have noticed that if you tell selenium to find an iframe html element, and then explicitly search within the WebElement of the iframe, that you can find child elements under it. However, without first finding each child iframe, Selenium does not seem to look inside the iframes without you explicitly helping it through the DOM.

JQuery does not have this limitation. It sees every registered DOM element just fine, and will grab it normally. Once you have the element as a JQuery object, you can build out a path of tags, parent by parent, all the way up the DOM. When the logic is complete, you will have a fully-qualified XPath from the top of the DOM down to the nested child element. Then, once this explicit XPath is supplied to Selenium, you are holding its hand down the rabbit hole through one or more iframes until it runs into the object you want.

Asyranok
  • 950
  • 1
  • 7
  • 17
  • 1
    Nice! Can’t believe you’ve got that on deck. Thanks for the quick solution. Totally forgot about jquery. – C. Peck Mar 12 '19 at 21:00