Monday, December 6, 2010

Webdriver Extensions

I was playing with Selenium2 Webdriver, and the IWebDriverExtensions class gives you the ability to extend the base api.  I used this to create an api more like selenium1.  Now when I create a new webdriver driver, I have access to all the following functions.  Being able to do things like driver.isVisible saved much time.


    public static class IWebDriverExtensions
    {
              public static void click(this IWebDriver driver, By bylocator)
        {
            driver.WaitForElementPresent(bylocator);
            IWebElement element = driver.FindElement(bylocator);
            element.Click();
        }
        public static void type(this IWebDriver driver, By bylocator, string text)
        {
            driver.WaitForElementPresent(bylocator);
            IWebElement element = driver.FindElement(bylocator);
            element.SendKeys(text);
        }

        // This is a basic wait for element not present a'la Selenium RC
        // but sharing the same timeout value as the driver
        public static bool isVisible(this IWebDriver driver, By bylocator)
        {
            try
            {
                IRenderedWebElement element;
                element = (IRenderedWebElement)driver.FindElement(bylocator);
                return element.Displayed;

            }
            catch (NoSuchElementException)
            {
                return false;
            }
        }
        public static bool isText(this IWebDriver driver, By bylocator, string textString)
        {
            string elementText = driver.FindElement(bylocator).Text;
            if (elementText == textString)
                return true;
            else
                return false;
        }
        public static bool isTextPresent(this IWebDriver driver, By bylocator, string textString)
        {
            string pageText = driver.PageSource.ToString();
            string elementText = driver.FindElement(bylocator).Text;

            if (elementText == pageText)
                return true;
            else
                return false;
        }
        public static bool isPresent(this IWebElement driver, By bylocator)
        {
            try
            {
                IWebElement element = driver.FindElement(bylocator);
                return true;
            }
            catch (NoSuchElementException)
            {
                return false;
            }
        }

        public static void WaitForElementNotPresent(this IWebDriver driver, By bylocator)
        {
            int timeoutinteger = Common.DriverTimeout.Seconds;

            for (int second = 0; ; second++)
            {
                Thread.Sleep(1000);

                if (second >= timeoutinteger) Assert.Fail("Timeout: Element not found: " + bylocator);
                try
                {
                    IWebElement element = driver.FindElement(bylocator);
                }
                catch (NoSuchElementException)
                {
                    break;
                }
            }
        }
        public static void WaitForElementPresent(this IWebDriver driver, By bylocator)
        {
            int timeoutinteger = Common.DriverTimeout.Seconds;

            for (int second = 0; ; second++)
            {
                Thread.Sleep(1000);

                if (second >= timeoutinteger) Assert.Fail("Timeout: Element not found: " + bylocator);
                try
                {
                    IWebElement element = driver.FindElement(bylocator);
                    break;
                }
                catch (NoSuchElementException)
                {

                }
            }
        }
        public static void WaitForElementNotVisible(this IWebDriver driver, By bylocator)
        {
            int timeoutinteger = Common.DriverTimeout.Seconds;

            for (int second = 0; ; second++)
            {
                Thread.Sleep(1000);

                if (second >= timeoutinteger) Assert.Fail("Timeout: Element still visible at: " + bylocator);
                try
                {

                    if (!driver.isVisible(bylocator)) break;

                }
                catch (NoSuchElementException)
                {
                    break;
                }
            }
        }
        public static void WaitForElementVisible(this IWebDriver driver, By bylocator)
        {
            int timeoutinteger = Common.DriverTimeout.Seconds;

            for (int second = 0; ; second++)
            {
                Thread.Sleep(1000);

                if (second >= timeoutinteger) Assert.Fail("Timeout: Element not visible at: " + bylocator);
                try
                {



                    if (driver.isVisible(bylocator)) break;

                }
                catch (NoSuchElementException)
                {
                    break;
                }
            }
        }
    }

Dealing with frames

So I see a lot of people struggling to figure out frames and selenium.  In my opinion, they are always a pain.  Many times they are dynamic, meaning they have a different id or name every time you run your tests.  Also, having to call selectFrame every few lines of code to go back and forth between functional areas is annoying, not to mention the pain of having to figure out what the frame names are.

I'm here to tell you that for the most part, you don't even need to deal with them.  The IDE throws in TONS of selectFrame commands when it records.  Your tests will work 3x better if you remove them all.  There are two things that selecting a frame really does. 
1) It limits your scope. 
2) It tells selenium what page you using.

1)  For the most part, limiting your scope is BAD.  It makes your tests less stable (if a button moves from one frame to another, you will have to change your tests, which should be avoided whenever possible).   In addition, any frame will AUTOMATICALLY look in all child frames for elements.  So if you select the main window frame, you will be looking through all subframes on the page.  You can do this with selectWindow("null").  Passing "null" tells selenium to look at the root frame, the one that was opened when you created a new browser.

2)  There is only one reason you need to select a frame: When you need to tell selenium what page you're referencing.  This happens for one of two reasons:
   A)  When a selenium command references the page.
   B)  When you have a popup or more than one window. 

A) A typical website has multiple pages, normally to separate navigation and content.  Commands like waitForPageToLoad, and verifyTextPresent use the currently selected page ONLY.  So you will need to selectFrame before you use these types of commands.  If you have the wrong frame selected, they will fail. 

B)  PopUp windows need to be selected to be interacted with.   Since there are typically only 1 popup open at a time, you can look for ANY popup, and avoid trying to determine the ID or name of the popup.  For example, by passing a blank string, selenium will return the first popup it finds.  I use this:
            selenium.WaitForPopUp("");
            selenium.SelectPopUp("");
 
In addition, besides PopUps, sometimes a new browser window or tab is opened with something like target=_blank.  The easiest way to find the window is by looking for the title, with something like this:  selectWindow("title=My Title").   Lastly you can call getAllWindowIds to get an array of ID's, and then call selectWindow with one of the returned values. 

Monday, November 29, 2010

Automatically launching Selenium server

Selenium is an amazing tool.  However, one of the most annoying things about it is having to deal with the selenium server.  For those who don't know, the selenium-server is a .jar that you run on the machine you will be testing on.  It has to be running for tests to pass.  What this means is that you either have to start/stop it each time you want to perform a test, or you have to leave it up and running on your machine constantly.  This can be an issue when you are dealing with a remote machine, you don't want to have to remote desktop into it, launch selenium-server.jar, and then run your tests.

So my first blog will be about how to add a SetUpFixture to your tests to automatically launch and close selenium-server.

I created a new class to contain the SetUpFixture.  For those who don't know, the SetUpFixture tag identifies a class to be run before/after all Fixtures.  I created a string called Common.seleniumDirectory which contains the location of my tests.  I did it this way to allow it to be more dynamic.  I also am having it launch a specific firefox profile, however you can modify the string to be whatever you want.

namespace SeleniumTests
{
    [SetUpFixture]
    public class Common_Setup_Fixture
    {
        // Runs before all tests
        public static Process seleniumServer;
        public static readonly ProcessStartInfo seleniumServerProcessStartInfo = new ProcessStartInfo("java", "-jar " + Common.seleniumDirectory  + "RemoteControl\\selenium-server-1.0.3\\selenium-server.jar -firefoxProfileTemplate \"" + Common.seleniumDirectory + "FirefoxProfile\"");
      
        [SetUp]
        public void SetUp()
        {
            seleniumServer = Process.Start(seleniumServerProcessStartInfo);
            Common.verificationErrors = new StringBuilder();
        }

        [TearDown]
        public void TearDown()
        {
            try
            {
                seleniumServer.CloseMainWindow();
            }
            catch (Exception)
            {
                // Ignore errors if unable to close the browser
            }
          
        }
    }

Thursday, November 4, 2010

Hello Everyone

Welcome to my selenium blog.  I will be posting a variety of topics focused on selenium and selenium2 test automation.