Smart click (part 3)

Smart click (part 3)

In this part I decided to show some examples of using the SmartClick() method just to make the way it works easier to understand.

Example 1 – the simplest. In 99% of cases you can use it without any parameters just like an IWebElement interface extension method:

public void GoogleSearch()
{
   IWebDriver driver = new FirefoxDriver();
   driver.Navigate().GoToUrl("http://www.google.com");
   IWebElement query, btnSearch;
   WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

   query = driver.FindElement(By.Name("q"));
   query.Clear();
   query.SendKeys("selenium");

   btnSearch = driver.FindElement(By.Name("btnG"));

   btnSearch.SmartClick();

   wait.Until((d) => { return d.Title.StartsWith("selenium"); });
   Assert.AreEqual("selenium - Google Search", driver.Title);
}

In this case SmartClick works like native method Click() with some additional functionalities, such as logging and exception handling. It verifies that the element is not null, is visible and enabled, ignores the precondition method, uses the first click action from the list of default ones (in our case it is IWebElement.Click(), but you can replace it with any other) and finishes its work (because the postcondition method always returns true and no verification is provided). Therefore, SmartClick() is equal to Click() plus event messages logging plus handling exceptions.

Example 2 – using IWebDriver. You can invoke the method with instance of IWebDriver as a parameter to make click actions more robust

btnSearch.SmartClick(driver);

The method after clicking the btnSearch element verifies that any of the following events has occurred: title or url address or page source of the page has changed, number of opened tabs or windows has changed, or an alert message is displayed. If any of the conditions is met it means that the click action has been performed over btnSearch. If not – you will have an error message in your log file (or you can rewrite this behavior according to your needs).

Example 3. If you pass an additional parameter to the method – number of iterations – it will try clicking the element as many times as the iterations parameter specifies until one of the conditions above is met.

btnSearch.SmartClick(driver: driver, iterations: 3);

Example 4. Or if you pass different click actions, it will try clicking the btnSearch with all these actions until the conditions above are met:

btnSearch.SmartClick(driver: driver, clicks: ClickAction.Click, ClickAction.DoubleClick, ClickAction.JSClick);

Example 5 – using a precondition delegate. One more way to invoke the method is to pass a precondition method to it like this:

public void GoogleSearch()
{
   IWebDriver driver = new FirefoxDriver();
   driver.Navigate().GoToUrl("http://www.google.com");
   IWebElement query, btnSearch;
   WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

   btnSearch = driver.FindElement(By.Name("btnG"));

   btnSearch.SmartClick(driver, x =>
      {
         try
         {
            query = driver.FindElement(By.Name("q"));
            query.Clear();
            query.SendKeys("selenium");
            return true;
         }
         catch (Exception e)
         {
            //add "SmartClick preconditions error: " + e.Message error message into your log file
            return false;
         }
      });

   wait.Until((d) => { return d.Title.StartsWith("selenium"); });
   Assert.AreEqual("selenium - Google Search", driver.Title);
}

Here the method does all actions inside the passed anonymous method first and then clicks btnSearch. In this example it is not very useful but it might help if btnSearch was disabled until you entered a query into the search field. In such case, you can perform some preconditional actions first and then click the button. It also may be useful if your web element is a part of a dropdown menu and you have to hover or click some parent menu item first to make the element visible.

Example 6. The same thing can be done with post-conditions:

public void GoogleSearch()
{
   IWebDriver driver = new FirefoxDriver();
   driver.Navigate().GoToUrl("http://www.google.com");
   IWebElement query, btnSearch;
   WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

   query = driver.FindElement(By.Name("q"));
   query.Clear();
   query.SendKeys("selenium");

   btnSearch = driver.FindElement(By.Name("btnG"));

   btnSearch.SmartClick(postcondition: x => { return wait.Until((d) => { return d.Title.StartsWith("selenium"); }); });

   Assert.AreEqual("selenium - Google Search", driver.Title);
}

In the example above, the driver will try clicking btnSearch until the title of the current page starts with the ‘selenium’ word.

Example 7 – custom click actions. Invoking the method in the following manner:

btnSearch.SmartClick(clicks: ClickAction.JSClick);

will make SmartClick working like SmartClcik() without parameters (see details above) but only using JavaScript function for performing click action instead of native IWebElement.Click() action. Of course, you can pass any other action or a set of actions instead of ClickAction.JSClick.

Example 8.1 – passing a parametrized method as a parameter. The next example shows invoking SmartClick with a function as a parameter which has its own parameters. Assume you have some method in your test class (which matches to CustomFunction delegate signature – bool Method(params object[])). Here it is:

public bool EnterSearchCriteria(params object[] obj)
{
   if (!(obj[0] is IWebElement) || obj[0] == null)
      throw new Exception("Wrong parameter input");
   if (!(obj[1] is string) || string.IsNullOrEmpty((string)obj[1]))
      throw new Exception("Wrong parameter input");
   IWebElement query = (IWebElement)obj[0];
   query.Clear();
   query.SendKeys((string)obj[1]);
   return true;
}

And here is the example of its usage:

public void GoogleSearch()
{
   IWebDriver driver = new FirefoxDriver();
   driver.Navigate().GoToUrl("http://www.google.com");
   IWebElement query, btnSearch;
   WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

   btnSearch = driver.FindElement(By.Name("btnG"));

   btnSearch.SmartClick(precondition: x => EnterSearchCriteria(driver.FindElement(By.Name("q")), "selenium"));

   wait.Until((d) => { return d.Title.StartsWith("selenium"); });
   Assert.AreEqual("selenium - Google Search", driver.Title);
}

Example 8.2. You can do the same thing with post-conditions as well. Assume you have another method with appropriate signature which waits for the page title to change:

public bool WaitForSearchResult(params object[] obj)
{
   if (!(obj[0] is IWebDriver) || obj[0] == null)
      throw new Exception("Wrong parameter input");
   if (!(obj[1] is string) || string.IsNullOrEmpty((string)obj[1]))
      throw new Exception("Wrong parameter input");
   WebDriverWait wait = new WebDriverWait((IWebDriver)obj[0], TimeSpan.FromSeconds(5));
   return wait.Until((d) => { return d.Title.StartsWith((string)obj[1]); });
}

You can invoke it in this way:

btnSearch.SmartClick(postcondition: x => WaitForSearchResult(driver, "selenium"), clicks: new[] { ClickAction.CursorClick, ClickAction.JSClick });

Example 9. In the example above there are multiple click actions are used in addition. You may combine all the method’s parameters as you wish to make the method working the way you need. For example:

btnSearch.SmartClick(driver, x => EnterSearchCriteria(query, "selenium"), x => { return wait.Until((d) => { return d.Title.StartsWith("selenium"); }); }, 2, 3, ClickAction.LeftMBClick, ClickAction.SendKeyEnter);

P.S. It is likely that you will never use all the method’s features, but actually it’s a good to know that you can.

 

Leave a Reply