Janis Veinbergs
Janis Veinbergs

Reputation: 6988

Manage frames in selenium C# WebDriver

Need to understand some better practices handling elements within frames. Selenium WebDriver only works within the context of a frame/main document. It cannot locate elements for other frames before I IWebDriver.SwitchTo() it

The pain is I constantly have to switch between frames. At no point I can trust on which frame IWebDriver currently is.

Can I have multiple IWebDriver instances (a clone or something) within a single browser instance or there is only 1 per browser instance? I imagine I could have IWebDriver Driver property, switched to particular frame, where I can trust I will find elements there. For some other class I would use IWebDriver Driver property on a different frame.

Moreover how should frames be handled when constructing Page Object with PageFactory.InitElements? I must switch IWebDriver to correct frame before finding elements. But will my Page Object class will always work, regardless of frame I afterwards switch to? I.e given 2 page objects from different frames - can I seamlessly use them in my tests not thinking about frames?

Couldn't find any best practices regarding frame management.

Upvotes: 1

Views: 1845

Answers (1)

Jordan
Jordan

Reputation: 659

It would be nice if iframe support in WebDriver was a little more robust, but there are ways to help the situation. In answer to your question about whether or not WebDriver will seamlessly handle iframe switching in your tests - it will not. You'll need to manage that switching on your own as needed even after PageFactory has been run against the page object.

One option that might be worth considering is creating some utility methods that handle switching in and out of iframes.

This means you can call into your iframe switching method prior to accessing the element and have it be found by the PageFactory. This logic can be in the Page Object so the test writer does not need to handle iframe switching in tests. Abstracting this level of complexity out of the tests will help them stay focused on the business cases. It will also help with maintenance since product changes involving these iframes can just be changed in the Page Object and then all tests consuming the Page Object will not need to be changed (with regards to frames).

An example:

public class MyPageObject
    {
        private readonly IWebDriver _driver;

        [FindsBy(How = How.Id, Using = "id")]
        private IWebElement _myWebElementInIFrame;

        public MyPageObject(IWebDriver webDriver)
        {
            PageFactory.InitElements(webDriver, this);
            _driver = webDriver;
        }

        public void DoStuff()
        {
            SwitchToIframe("myiframe");
            _myWebElementInIFrame.Click(); //element found using PageFactory data now
            SwitchToDefaultContent();
        }

        public void SwitchToIframe(string frameName)
        {
            _driver.SwitchTo().Frame(frameName);
        }

        public void SwitchToDefaultContent()
        {
            _driver.SwitchTo().DefaultContent();
        }
    }

You could take this a bit further using some method templating:

public class MyPageObject : BasePageObject
    {

        [FindsBy(How = How.Id, Using = "id")]
        private IWebElement _myWebElementInIFrame;

        public MyPageObject(IWebDriver webDriver)
            : base(webDriver)
        {
            PageFactory.InitElements(webDriver, this);
        }

        public void DoStuff()
        {
            ExecuteActionInIFrame(() =>
            {
                _myWebElementInIFrame.Click(); //element found using PageFactory data now
            }, "myiframe", "mynestediframe");
        }
    }

    public abstract class BasePageObject
    {
        protected IWebDriver WebDriver { get; }

        protected BasePageObject(IWebDriver webDriver)
        {
            WebDriver = webDriver;
        }

        public void ExecuteActionInIFrame(Action action, params string[] frameNames)
        {
            foreach (string frameName in frameNames)
            {
                WebDriver.SwitchTo().Frame(frameName); //switch into multiple nested iframes in passed in order
            }

            //perform action now that we're in proper iframe
            action();

            //bounce out to root to have a standard starting point for next action
            WebDriver.SwitchTo().DefaultContent();
        }
    }

Another tip that helped me was you can get the name of a current iframe via some javascript. This might help you determine where exactly you are in the overall DOM while traversing iframes if you need that sort of information.

Hope this helps.

Upvotes: 1

Related Questions