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.

 

Adjustment of testing environment via Registry

Sometimes  you have to adjust parameters of your testing environment before running your tests. It may be some security settings, turning on or off some plug-ins, settings of browsers or other applications. As an example let’s look at the settings of Internet Explorer browser. Using this browser many automation testers often face with the following error: “Unexpected error launching Internet Explorer. Protected Mode settings are not the same for all zones. Enable Protected Mode must be set to the same value (enabled or disabled) for all zones. (NoSuchDriver)” 12.1

This error occurs when Protected  Mode for different zones are not the same. This is old and known issue which is easy to handle off. To solve the issue you just have to turn on Protected Mode for all zones or turn it off. 12.2

You can do it manually or programmatically using InternetExplorerOptions class from OpenQA.Selenium.IE namespace. Here I took this issue as an example to show you how you can adjust IE setting and many other things changing values of your system registry. Default setting of Internet Explorer browser are not the same for all zones and every time you have set the IE to its default (this is recommended to do before running tests) you have to adjust Protected Mode again. And if you have to do it on remote machine and especially on many machines it becomes boring. Fortunately there is an elegant solution. All these settings (and many other) may be set programmatically. Before starting working Internet Explorer reads its settings from system registry. So, if you put correct values to the registry the browser will be parametrized just as you need. Now a couple of words how to work with system registry. С# has special class named Registry for that, this class is located in  Microsoft.Win32 namespace. There are a lot of examples of using this class in the Internet, so I do not see any sense to describe it here one more time. I just want to show you my own solution. You have to create additional application for working with registry. Create new solution in Visual Studio with a project of Console Application type. 12.3

Give a name for your namespace and class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Win32;

namespace Assign_Environment_Settings
{
    class Program
    {
        static void Main()
        {
        }
    }
}

Add enumerator RegistryBranch to class Programm. It will be used for switching between branches of system registry

internal enum Branches
{
    HKLM,
    HKCU
}

Add struct KeyValueDescr, it will be representing one value for the registry

internal struct KeyValueDescr
{
    internal string key;
    internal string value;
    internal string message;
}

Add struct RegistryEntry, this is one registry entry

internal struct RegistryEntry
{
    internal string path;
    internal KeyValueDescr[] values;
}

And now add a method for setting in a value to the registry

internal static void SetRegistryValue(Branches branch, RegistryEntry[] settings, int delay)
{
    //declaration of an instance of registry key
    RegistryKey key = null;

    //block using is utilized for closing the registry after work and releasing used resources as RegistryKey implements interface IDisposable. Details are here : (http://msdn.microsoft.com/en-us/library/system.idisposable.aspx)
    using (key)
    {
        //looking over all setting that should be set
        foreach (var setting in settings)
        {
            //switching between registry branches.
            switch (branch)
            {
                case Branches.HKLM:
                    key = Registry.LocalMachine;
                    break;
                case Branches.HKCU:
                    key = Registry.CurrentUser;
                    break;
            }
            try
            {
               if (key != null)
               {
                   key = key.OpenSubKey(setting.Path, true);
                   foreach (var v in setting.values)
                   {
                       if (key != null)
                       {
                            try
                            {
                                key.SetValue(v.key, v.value);
                                Console.WriteLine(v.message);
                            }
                            catch (Exception ex)
                           {
                               Console.WriteLine(ex.Message);
                           }
                       }
                      else
                          throw new Exception("Registry branch " + setting.Path + " does not exist or is not available for editing!");
                  }
              }
          }
          catch (Exception ex)
          {
              Console.WriteLine(ex.Message);
           }
           Thread.Sleep(delay);
        }
    }
    Console.WriteLine("Done!");
}

Now before the method will be invoked you should describe necessary settings (in our case this is Protected Mode in IE). Inside Main() method let’s declare and initialize the settings:

RegistryEntry[] ie_settings =
 {
     new RegistryEntry
     {
         path = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1",
         values = new [] 
         {
             new KeyValueDescr
             {
                 key = "2500",
                 value = "0",
                 message = "Protected Mode for Local Intranet zone: enabled"
             }
         }
     },
     new RegistryEntry
     {
         path = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2",
         values = new [] 
         {
             new KeyValueDescr
             {
                 key = "2500",
                 value = "0",
                 message = "Protected Mode for Trusted Sites zone: enabled"
            }
        }
    },
    new RegistryEntry
    {
        path = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3",
        values = new [] 
        {
            new KeyValueDescr
            {
                key = "2500",
                value = "0",
                message = "Protected Mode for Internet zone: enabled"
            }
        }
    },
    new RegistryEntry
    {
        path = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\4",
        values = new [] 
        {
            new KeyValueDescr
            {
                key = "2500",
                value = "0",
                message = "Protected Mode for Restricted Sites: enabled"
            }
        }
    }
};

Now the method SetRegistryValues may be invoked:

const int delay = 1000;
Console.WriteLine("ENVIRONMENT SETTINGS\r\n");
Console.WriteLine("Starting to assign Internet Explorer settings...");
Console.WriteLine("------------------------------------------------");

SetRegistryValues(Branches.HKCU, ie_settings, delay);

Console.WriteLine("\r\nAll changes done!");
Thread.Sleep(delay * 10);

Now I will explain where the values used above are taken from. To know what values to use you should know what is changing in the registry when you do some actions in the system. The program RegFromApp can help. There are 64 and 32-bit versions of the program. Located here. There are good documentation of the program on the site. You can read it for some details. Here I describe only necessary for current project features. Open the application which you are going to watch for (in my case it is Internet Explorer browser). Select Tools -> Internet Options

12.4

Then Security tab

12.5

Adjust all settings you are about to change in wrong position. For example turn off Protected Mode for all zones. Now launch appropriate version of RegFromApp. In ‘select process to inspect’ window select process of the Internet Explorer – IEXPLORE.EXE.

12.6

Return back to IE and turn on Protected Mode for all zones, click Apply. In RegFromApp window you can see the following values:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\4] “2500”=dword:00000000

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2] “2500”=dword:00000000

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1] “2500”=dword:00000000

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3] “2500”=dword:00000000 12.7

That’s it. You’ve got the path, key and value for all changed settings of Protected Mode. Later when you start the IE this values will be read by the browser from system registry. Now you can put this values in your RegistrySettings application, recompile the code and to launch it from your automated tests or manually. To run the program automatically invoke the code below right before running your tests. For example:

const int timeoutSec = 20;
var regSettings = new ProcessStartInfo
{
    FileName = "C:\\pathToTheProgram\\RegistrySettings.exe"
};
Process proc = Process.Start(regSettings);
int ticker = 0;
while (proc != null && (!proc.HasExited || ticker < timeoutSec))
{
    Thread.Sleep(1000);
    ticker++;
}

You can do it in a method with attribute [TestFixtureSetUp] or in a parent class of your test classes or via separate script. Playing with RegFromApp you can get a lot of additional information about system registry and create automated adjustments for other programs and applications that use the registry for storing their settings. Note that if current user of your operating system may have limited permissions for editing the registry settings. An attempt to change such values will throw an exception with error message like “Registry branch Softaware\Microsoft\Internet Explorer\Main does not exist or is not available for editing!”. To avoid it your current user should have system administrator permissions. Also sometimes the system firewall or antivirus program may deny you to change the registry or may require additional confirmation. It may be adjusted in settings of appropriate program.

Seven Steps For Successful Outsourcing

Outsourcing work to overseas is common practice to save money. Sometimes, you get the most out of your work by sending work to low cost resources. You can utilize saved money to put into various initiatives like in infrastructure, hiring skilled reviewers for code review etc. Sometime sending work to overseas can be disastrous for your company too and you can end up either incomplete project or wasting all money with no result. You may have placed all the checks at every level to make sure that project complete successfully, but later you will start noticing some unforeseen events, those you never expected, and you will pay a high price to overcome those situations. While making contracts, outsourcing companies’ sales people over promise you for your project with lowest cost and you decided to go for that. Later they start giving some genuine excuses that you cannot ignore and you pay more to fix that. As a result of that you outsourcing cost increases and it is kind of never ending process.

For a successful outsourced project, you need to take care many things, beginning from selecting the outsourcing company to getting the project delivery.

Check the background of the company you are outsourcing:

The moment you let your vendors know that you are looking for work to outsource, you will start getting proposals. Sales managers will give you presentations that they have the experience and expertise to handle this kind of project. They can do the work quickly on the lowest budget possible. Do not get into the trap of such promises. You will get what you are paying for. It doesn’t mean that you need to pay a high price to get good quality work. There are many good companies where you can get quality work out of them with lower cost. You can find about them with your reference. Ask venders to provide their client list where they have done are currently working on same nature of projects. Try to find some link in that company. LinkedIn can be a good tool to find some reference. You can contact reference to understand vendor’s work commitments and their experience with the vendor. Go through their client vendor agreement. See if they have any certification or affiliation needed for the project. There are many more, check point which can help to Choose right vendor. s http://operationstech.about.com/od/costsavingstrategies/u/Outsourcing-Vendor-Selection.html

http://www.tutorial-reports.com/business/outsourcing/criteria.php

http://www.cio.com/article/2432212/outsourcing/how-to-choose-the-right-it-outsourcing-provider.html

Double check with resources for the project:

Along with selecting the right vendor so you need to finalize suitable resources for the team too. Usually, project demo or proposals are prepared by pre-sales engineer or dedicated resources for new clients. They are quite the experts in doing that. Do not judge working resources and vendor based on the demo. I am not trying to be cynical, but you have to accept this truth. Ask vendors to provide the detailed resume of each resource who are going to work on the project. Check their skill set if these are meeting the minimum needed requirement. Make sure they have done similar work in the past. You may not need all resources equally skilled. choose resources considering many aspects like every aspect like technical skill, teamwork, work ethics etc.

Select the bridge between you and sourcing team:

Selecting coordinator between the team is equally important as selecting vendors and resources. You may be sending your work to another country where English is not the first language like India, China or Ukraine etc. where you can find qualified people but not expert in English. There may be some cultural differences too. You need to select someone from either side who has an experience working as coordinator in the past or have a better understanding of the culture. This will help you in the long run for the project. You should consider having least one trip to the site if budget allows or bring someone at your location to a few weeks. This will help them to understand your companies work culture and expectations from them. Coordinator can help the team based on past experience to explain the problem. Sometime its difficult to explain some critical project strategy and you need to reiterate a number of times where your coordinator can explain them on your behalf. If the vendor is working on another time zone than you coordinator needs to be involved in handling this time zone difference and keep the flow regardless of any time zone difference.

http://www.greatsampleresume.com/Job-Responsibilities/Onsite-Coordinator-Responsibilities.html

http://auriga.com/blog/blog/2013/08/09/one-of-the-lesser-understood-roles-in-distributed-teams-the-onsite-coordinator/




Cross Cultural Training for outsourcing team:

If your budget allows then have some money aside to have cross-cultural training. As I said in the previous section if you are outsourcing our work to another country with cultural difference then this kind of training will help you on a daily basis. They will understand how you want them to respond in any situation. Office culture in India is different as compared to US. They way of responding in email is also different for the same situation. It is very important to understand the culture of each other and these things increase the chance of success of the work. It’s not necessary if resources have good experience with working similar project.

http://ieeexplore.ieee.org/xpl/articleDetails.jsp?reload=true&arnumber=6276345

http://www.wi-frankfurt.de/publikationenNeu/ManagingCrossCulturalDynamics2721.pdf

Consider outsourcing team as a part of your team:

Treat your new team as your own team. Sometime we outsource monotonous kind of work, which your employees do not want to do like running regression tests suites on a daily basis. If this work is monotonous for your team then it could be same for them after a month or so. Try to send some interesting work too. Like every other employee they are also looking for some interesting work and learn new things. Give them window of learning new and working on interesting things will encourage them. These will help to increase the productivity of the team. Many managers feel that they outsource the project for low cost mean that they can use contractors to get the maximum work out of them. That is really a very bad strategy. Give them the opportunity to learn and welcome any kind of improvement they are offering. Consider their proposal and ask them to implement if they are capable of. Let them feel that they are equally important as other team in your company. Research shows that happy employees give maximum productivity at work.

Have Contingency Plans Ready:

Attrition is the biggest problem you should be ready to deal with. Most vendors are facing this problem with their employees. Many managers experienced 100% turn around in their team within a year. Some will not give you enough time to do knowledge transfer to replacement member. You need to prepare yourself for any unforeseen situation. Make sure you have work rotations in your team to do various kinds of task to avoid any boredom. Make sure each one of you team can do all kinds of task in case of any situation. Do not try to make a dependency on one person. Distribute the work. Periodically conduct cross-functional training in team. In case of a sudden exodus of team member you will not be impacted severely.

http://www.callcentrehelper.com/top-5-ways-to-improve-attrition-in-your-contact-centre-73.htm

http://blog.talkdesk.com/10-steps-to-help-reduce-agent-attrition-in-call-centers

http://smallbusiness.chron.com/tips-prevent-attrition-organization-23517.html

Well defined process:

Make sure you have defined all plans at his place. Have a well defined project process. Document points of contact that may require any kind of involvement in the project. Defined proper checkpoints for review. Have best practices and lesson learned document ready so you can save time to review. If possible, before start of project conduct training about the process. Go through all documents and explain the team how to follow the flow and how to define work-completed criteria. Try to conduct various kinds of reviews each delivered work. Have lesson-learned meeting.

http://www.researchware.com/support/50-resources/hr-studies/159-ngwenyama-sullivan.html

http://books.google.com/books?id=CGB7ov2p16IC&pg=PA96&lpg=PA96&dq=secrets+of+a+successful+outsourcing&source=bl&ots=S6av1X6jk_&sig=SFjeJjHiwuQoxuB4hfWomvpQwKk&hl=en&sa=X&ei=dhfoU8fwBdejyASn9IKICg&ved=0CCMQ6AEwAQource=bl&ots=S6av1X6jk_&sig=SFjeJjHiwuQoxuB4hfWomvpQwKk&hl=en&sa=X&ei=dhfoU8fwBdejyASn9IKICg&ved=0CCMQ6AEwAQ

http://entrepreneurs.about.com/cs/beyondstartup/a/uc041003a.htm

Overall outsourcing work is not that easy as we think. You should consider these points as initially thought before taking decision of outsourcing. There are many more point you should consider which is actually not given here but exist and that depends upon the process you defined in past.

Please share your experience about outsourcing? Do not forget to share.