Two options for copying files are below.

  • Robocopy - More details can be found at Robocopy
  • Copy_Item cmdlet - More details can be found at Copy-Item

Copying Folder structure Only

1
2
3
4
5
6
7
8
9
$source = "C:\tools\DevKit2\octopress-blog\source"
$dest = "D:\delete"
Copy-Item $source $dest -Filter {PSIsContainer} -Recurse -Force

#OR
robocopy $source $dest /e /xf *.*

# /e denotes all folder including empty folders. /xf denotes all files except one of format *.*
# /e can be replaced with /s for ignoring empty folders

Flattening Folder structure - Copy all files from nested folders to a single folder

1
2
3
4
5
6
7
8
$source = "C:\tools\DevKit2\octopress-blog\source"
$dest = "D:\delete"
# Below is required only if we need to create destination folder. Uncomment below line if folder needs to be created
#New-Item $dest -type directory 

Get-ChildItem $source -Recurse | `
    Where-Object { $_.PSIsContainer -eq $False } | `
    ForEach-Object {Copy-Item -Path $_.Fullname -Destination $dest -Force} 

Copy same folder structure

1
2
3
$source = "C:\tools\DevKit2\octopress-blog\source"
$dest = "D:\delete"
robocopy $source $dest /e

Git for windows is normally shipped with long path support disabled due to mysys not supporting file path/name greater than 260 character. While cloning repository with large nested directory structute may cause error “file name too long”. This can be fixed by below command. It can be executed using powershell or cmd directly in project ( or anywhere if git variable is available)

cmd.exe
1
git config --system core.longpaths true

Code Snippet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
   private void RunCLIjobsOnLocal(string arguments, int WaitTimePerCommand)
        {
            var psi = new ProcessStartInfo();
            psi.CreateNoWindow = true; //This hides the dos-style black window that the command prompt usually shows
            psi.FileName = @"cmd.exe";
            psi.Arguments = "/C " + arguments;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardError = true;
            psi.UseShellExecute = false;
            var sspw = new SecureString();
            foreach (var c in password)
            {
                sspw.AppendChar(c);
            }
            psi.Domain = domain;
            psi.UserName = userName;
            psi.Password = sspw;
            psi.WorkingDirectory = @"C:\";

            using (Process process = new Process())
            {
                try
                {
                    process.StartInfo = psi;
                    process.Start();
                    var procId = process.Id;
                    string owner = GetProcessOwner(procId);
                    // Synchronously read the standard output of the spawned process. 
                    StreamReader reader = process.StandardOutput;
                    string output = reader.ReadToEnd();
                    reader = process.StandardError;
                    string error = reader.ReadToEnd();
                    if(error.Length >0)
                       process.WaitForExit();
                }
                catch (Exception e)
                {
                    log.Error(e.Message + "\n" + e.StackTrace);
                }
            }
        }

        private string GetProcessOwner(int processId)
        {
            string query = "Select * From Win32_Process Where ProcessID = " + processId;
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
            ManagementObjectCollection processList = searcher.Get();

            foreach (ManagementObject obj in processList)
            {
                string[] argList = new string[] { string.Empty, string.Empty };
                int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
                if (returnVal == 0)
                {

                    return argList[1] + "\\" + argList[0];
                }
            }
            return "NO OWNER";
        }

In previous post, I have mentioned different ways of identifying web elements using XPath . Very often , we will have to identify child elements while automating using selenium. Let us consider below example . This is an HTML layout of table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<table id=table1 style="width:100%">
  <tr>
    <td>John</th>
    <td>Smith</th>
    <td>50</th>
  </tr>
  <tr>
    <td>Jill</td>
    <td>Smith</td>
    <td>50</td>
  </tr>
  <tr>
    <td>Eve</td>
    <td>Jackson</td>
    <td>94</td>
  </tr>
</table>

Assuming we need to iterate across all the rows to identify which row have the name “Eve” and then do some action on that row . This can be achieved by below

1
2
3
4
5
6
7
8
9
10
11
 // Identify the table header using ID
 IWebElement e = driver.FindElement(By.Id("table1"));

 /* Identify all child nodes for the webelement e (table) . Note the "." in XPath which means to search within current node. */

 IList<IWebElement> rowlist = e.FindElements(By.XPath(".\\tr"));
 foreach(var row in rowlist){
 IList<IWebElement> collist = row.FindElements(By.XPath(".\\td"));

 // Now iterate over each value in collist to check whether it have "Eve".
 }

Important step here is to use “.” in XPath so that selenium limit its search for current node rather than everywhere on document. This will help to identify elements with respective to another element.

Further details of how to use XPath can be found in https://www.w3schools.com/xml/xpath_syntax.asp

Main ones are

  • / - Search from root
  • // - Search anywhere in document which match selection
  • . - select current node
  • .. - select parent of current node

Very frequently testers will meet a situation where they need to take screenshot of webpage they are testing , either for base line or as a proof of test result. This is same with automated testing . Even though automated test cases have their own of way of publishing test results, it is always desirable to keep a proof of result.. Screenshot come to help in this regards. In this blog post , I will explain , how to take a screenshot with Selenium Web Driver with C#. In future I will add another couple of post to explain , how to consolidate the screenshots into a PDF document.

In .NET binding, we have an interface called ITakesScreenshot , which helps to capture screenshot of active window. Below code will help to take screenshot and save it in the path specified while calling the function

1
2
3
4
5
6
7
8
9
10

// PathToFolder is the location where we need to save the screenshot
// FileName is another string where PathToFolder is appended with timestamp
public void TakeScreenShot(string PathToFolder)
{
  string fileName = PathToFolder + DateTime.Now.ToString("HHmmss") +".jpeg";
  Screenshot cp = ((ITakesScreenshot)driver).GetScreenshot();
  cp.SaveAsFile(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);

}

During testing we will sometimes come up to situations where developers are not following best practises for testability . We will frequently come up situations where elements doesn’t have any unique identifiable property. XPath axis comes to help in those situations. We can identify elements using various XPath Properties

List of various XPath Axis are available in https://developer.mozilla.org/en-US/docs/Web/XPath/Axes If you have well-defined properties to identify the element, use them as your locator. Please read  locator strategy  Using XPath and Other Parameters

Below are major one’s which we will frequently use

1. ancestor

This selects all ancestors of current node. That will include parent, grand parents etc Eg :

1
 //td[text()='Product Desc']/ancestor::tr

2. descendant

This selects all children of current node. That will include child, grand child etc Eg:

1
/table/descendant::td/input

3. followingis

Th selects everything after the closing tag of current node Eg:

1
//td[text()='Product Desc']/following::tr

4. following-sibling

This selects all siblings after the closing tah of current node. Eg:

1
//td[text()='Product Desc']/followingsibling::td

5. preceding

This selects everything prior to current node Eg:

1
 //td[text()='Add to cart']/preceding::tr
  1. preceding-sibling

This selects all siblings prior to current node Eg:

1
//td[text()='Add to cart']/precedingsibling::td

7. child

This selects all children of current node

8. parent

This select parent of current node

As usual , you can always use combinations of above in your test. Statements can be constructed in the same way as we traverse the XPath axis

Last , but not least… we can also use regular expression in XPath.

XPath is XML query language which can be used for selecting nodes in XML. Hence it can be used to identify elements from DOM since they are represented as XHTML documents. Selenium WebDriver also supports XPath for locating elements. They also help to look for elements in both direction and hence it is generally slow compared to all other locator strategy. We can use XPath with both absolute path and relative path.

Absolute XPath

Absolute Path refers to specific location in DOM, by considering it’s complete hierarchy. However this is not an ideal locator strategy since it makes your test very brittle. The absolute path will change if there is any change/realignment etc in UI.

Example of Xpath using absolute path is as below

1
WebElement userId = driver.FindElement(By.XPath(html/body/div[2]/div/form/input[2]));

Relative XPath

With relative Path , we can find element directly without entire structure. It helps to look out for any elements which matches with specified relative path . Example for a relative path based locator strategy is as below.

Note: Relative XPath starts with “//”

1
2
3
4
WebElement userId = driver.FindElement(By.XPath("//input"));
// This retrieve first element with input tag.
WebElement userId = driver.FindElement(By.XPath("//input[2]"));
// This retrieve second element with input tag.

Relative XPath  - With Attributes

If we need to further narrow down our location strategy, we can use Attributes along with relative XPath. There may be situations where we need to multiple attributes to uniquely identify an element. We can also specify locators to identify for ANY attribute

1
2
3
4
5
6
7
WebElement passwordField = driver.FindElement(By.XPath("//input[@id='password']"));
// Above will identify first element with input tag which also has id as "password".

WebElement LoginButton = driver.FindElement(By.XPath("//input[@type='submit'and @value='Login']"));
//Note you can use "or" as well.
WebElement someField = driver.FindElement(By.XPath("//input[@*='password']"));
// Above will identify first element with input tag which also has any attribute as "password".

Relative XPath - Partial Match

Sometimes there may be situations where element attributes like ID are dynamically generated. Those will generally have some unique part in attributes likeID and remaining will be generated dynamically , which will keep on changing. This will need a locator strategy which will help us to identify elements using partial match. Main types are

  • starts-with()

  • ends-with()

  • contains()

1
2
3
4
5
6
7
WebElement passwordField1 = driver.FindElement(By.XPath("//input[starts-with(@id,'password')]"));
// Above will identify first element with input tag which also has id starting with "password".
WebElement passwordField2 = driver.FindElement(By.XPath("//input[ends-with(@id,'password')]"));
// Above will identify first element with input tag which also has id ending with "password".

WebElement passwordField3 = driver.FindElement(By.XPath("//input[contains(@id,'password')]"));
// Above will identify first element with input tag which also has id containing with "password".

Assert.AreEqual vs Assert.AreSame

Very frequently I use Assert.AreEqual and Assert.AreSame for doing assertions in the code. Below is high level difference between both.

Assert.AreSame

Assert.AreSame checks whether both comparing objects are exactly the same ( reference indicate same object in memory) .It is normally known as Reference Equality

Assert.AreEqual

Assert.AreEqual checks whether both objects contain same value. It is normally known as Value Equality. For primitive value types ( like int, bool) this is straight forward. But for other types ( especially user defined objects) , it is depends on how the type defines equality.

Hence Assert.AreEqual will fail ( most of the time) when we compare two objects. This link have some discussion point about the same.

Locators are html properties of a web element , which can be considered as an address of the element. An element will have various html properties. We can use Firebug extension or Chrome dev tools to identify different locators of an element.

Selenium Web Driver provides two different methods for identifying html elements .

  • FindElement  for WebDriver and WebElement Class. When locating element matching specified criteria, it looks through DOM( Document Object Model) for matching element and return the first matching element. If there are no matching element, it will throw NoSuchElementFoundException

  • FindElements for WebDriver and WebElement Class. When locating element matching specified criteria, it looks through DOM( Document Object Model) for matching element and return a list of all matching element. If there are no matching elements, then it will return an empty list .

Note: Both of them doesn’t support regular expression for finding element. Simple way to do that will be to get list of all elements and then iterate to find a matching regular expression

There are multiple criteria which we can use for looking for an element. FindElement and FindElements work exactly same way except for above difference. Different critieria are

  • driver.FindElement(By.Id())

  • driver.FindElement(By.Name())

  • driver.FindElement(By.ClassName())

  • driver.FindElement(By.TagName())

  • driver.FindElement(By.LinkText())

  • driver.FindElement(By.PartialLinkText())

  • driver.FindElement(By.CssSelector())

  • driver.FindElement(By.XPath())

Example:

1
2
3
4
5

WebElement _firstElement = driver.findElement(By.id("div1"));
WebElement _secondElementInsideFirstOne =_firstElement .findElement(By.linkText("username"));

IList<IWebElement> elements = driverOne.FindElements(By.ClassName(<span class="pl-s"><span class="pl-pds">“</span>green<span class="pl-pds">“</span></span>));

Using any attributes other than XPath and CssSelector are straight forward. More about using XPath and CssSelector in next blog.

In Specflow, Step definitions are global. So a scenario can have multiple step definitions which can be present in different classes.  Sometimes, there arise a need to share the data between steps residing in different classes. How do we do it??

There are multiple ways to do it

  1. Context Injection

  2. Feature Context

  3. Scenario Context

Let us look into more details about how to store and retrieve data using Scenario Context .

ScenarioContext.Current

How do we add a key value pair to Scenario Context ? It is as simple as below

1
2
3
4
5
6
[Given(@"I have entered (.*) and (.*) into the Login Page")]
public void GivenIHaveEnteredAndIntoTheLoginPage(string p0, string p1)
 {
 ScenarioContext.Current.Add("username", p0);
 ScenarioContext.Current.Add("password", p1);
 }

How do we retrieve the value from ScenarioContext ?

1
2
3
4
5
6
7
When(@"I press retrieve data")]
public void WhenIRetrieveData()
{
string username = (string)ScenarioContext.Current["username"];
string password = (string)ScenarioContext.Current["password"];

}

Note: While retrieving , scenarioContext.Current always return an object . Hence we need use explicit casting while retrieving data from scenario context.

In Nut Shell,

**Set a value for a key ( Store data ) ** ScenarioContext.Current.Add(string key, object value); **Get a value of the key ( Retrieve data) ** var value =(Type) ScenarioContext.Current.[string Key]; var value = ScenarioContext.Current.Get(string Key);

We can use this for storing and passing objects as well

Example of storing webdriver object is as below.(where "browser" is current webdriver object ) ScenarioContext.Current.Add("driver1", browser); IWebDriver driver2 = (IWebDriver)ScenarioContext.Current["driver1"];