Code & QA

More Extension Method

SwitchToFrame()

Method SwitchToFrame() is implemented as IWebDriver extension method. Obviously it is located inside the Extensions class. It takes an array of objects as input parameters. I assume that the objects are paths of the frames the driver must be switched to. Type of objects must string, integer or OpenQA.Selenium.By. If the array is not empty the method will switch the driver consequentially into every frame. So, assumed that every next item of the array is located inside the previous. Thus, frame[0] is located on the very top of the page, frame[1] is located inside Frame[0], frame[2] is inside frame[1],

frame[n] is inside frame[n -1]

/// <summary>

/// Switches driver sequently to specified frames

/// </summary>

/// <param name=”driver”>instance of IWebDriver</param>

/// <param name=”frames”>sequence of locators of iframes which the driver must be switched into</param>

public static void SwitchToFrame(this IWebDriver driver, params object[] frames)

{

//initially the driver is switched to the top level of a page

driver.SwitchTo().DefaultContent();

//if frames array is empty or it is null there is nothing to switch into. So, return

//By the way, you can guess that invoke of the method without parameter just switches the driver to the top of page DOM structure

if (frames == null || frames.Length == 0)

return;

//in the loop the method consequentially finds every frame from the array and switches the driver into it

foreach (var frame in frames)

{

//first the method tries to find the frame by its id or name

if (frame is string)

{

string s = frame.ToString();

try

{

//if a frame with specified id of name is found, the driver will be switched into it, and then the next frame from the array will be taken

//otherwise an exception will be thrown

driver.SwitchTo().Frame(s);

Report.AddInfo(“Switching into iframe ” + s);

continue;

}

catch (Exception ex)

{

Report.AddWarning(“Failed to switch into iframe ” + s, “Web driver is switched into iframe ” + s, ex.Message);

break;

}

}

//the method tries to switch the driver into the frame by its index

else if (frame is int)

{

int i = (int)frame;

string s = i.ToString();

try

{

if (i < 0)

throw new ArgumentOutOfRangeException();

driver.SwitchTo().Frame(i);

Report.AddInfo(“Switching into iframe ” + s);

continue;

}

catch (ArgumentOutOfRangeException)

{

Report.AddWarning(“Failed to switch into iframe ” + s, “Web driver is switched into iframe ” + s, “Index of frame cannot be negative. Verify input parameters”);

break;

}

catch (Exception ex)

{

Report.AddWarning(“Failed to switch into iframe ” + s, “Web driver is switched into iframe ” + s, ex.Message);

break;

}

}

//with frame locator. As an iframe is common web element it may be represented as IWebElement interface, hence it may be found on a page like any other IWebElement instance – with OpenQA.Selenium.By locator.

else if (frame is By)

{

By locator = (By)frame;

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(Globals.timeoutForWaitingElement));

try

{

wait.Until(ExpectedConditions.ElementIsVisible(locator));

IWebElement e = driver.FindElement(locator);

driver.SwitchTo().Frame(e);

Report.AddInfo(“Switching into iframe by locator ‘” + locator.ToString());

continue;

}

catch (Exception ex)

{

Report.AddWarning(“Cannot switch to iframe by locator ” + locator, “Web driver is switched to iframe by locator” + locator, ex.Message);

}

}

else

Report.AddError(“Input parameter ” + frame.ToString() + ” is incorrect”, “Input parameter type must be String either Integer or OpenQA.Selenium.By”, “Type of input parameter ” + frame.ToString() + ” is ” + frame.GetType());

}

}

IWebDriver and IWebElement interfaces implement interface ISearchContext, which implements methods FindElement(By locator) and FindElements(By locator).

FindElement() seeks for a web element on a web page using OpenQA.Selenium.By locator passed as input parameter. The method returns the first web element which location in DOM structure of the page matches to the locator. If no element with expected criteria is found, NoSuchElementException is thrown.

FindElements() method does not throw NoSuchElementException, instead it gathers into ReadOnlyCollection all web elements on the page that match to the locator and then returns the collection. If no suitable element is found just empty collection is returned.

I wrapped the methods with my own ones for easier finding web elements on a page. My methods are called GetWebElement() and GetWebElements(). Both are extension methods for ISearchContext interface.

GetWebElements()

/// <summary>

/// gets a web element collection located on a page by specified parameter. If no element is found the method makes second attempt

/// </summary>

/// <param name=”driver”>Instance of ISearchContext. Actually it may be either IWebDriver or IWebElement</param>

/// <param name=”elementType”>Type of targeted element(s). This parameter is used only for reproting</param>

/// <param name=”elementName”>Name of sought-for element (is for reporting only as well)</param>

/// <param name=”waitTimeout”>Timeout between first and second attempt to find the element</param>

/// <param name=”locators”>List of locators to be used for finding the element</param>

/// <returns>Returns a collection of IWebElements</returns>

public static ReadOnlyCollection<IWebElement> GetWebElements(this IWebDriver driver, Type elementType = null, string elementName = null, int? waitTimeout = null, params ElementLocator[] locators)

{

bool continueSearch = false;

string type = elementType == null ? “IWebElement(s)” : elementType.Name;

string name = elementName ?? “Undefined”;

int timeout = (waitTimeout == null || waitTimeout < 0) ? Globals.timeoutForWaitingElement : (int) waitTimeout;

ReadOnlyCollection<IWebElement> elements = null;

SwitchToFrame(driver);

do

{

foreach (var locator in locators)

{

try

{

//if driver parameter is IWebDriver it may be switched between frames and if sought-for element(s) is/are inside iframe the driver will be switched into it. Otherwise, if ISearchContext is IWebElement, no switching into frames is possible

if(locator.Frames != null)

{

SwitchToFrame(driver, locator.Frames);

}

elements = driver.FindElements(locator.Locator);

//if no element is found, another locator will be taken for searching until all locator will be verified or element(s) will be found – continue statement

if(elements.Count == 0)

{

Report.AddInfo(“No element is found using locator ” + locator.Locator);

continue;

}

//otherwise – when element(s) is/are found the method finishes its work and the collection is returned

Report.AddInfo(elements.Count + ” ” + type + ” ” + elementName + ” element(s) found using locator ” + locator.Locator);

return elements;

}

catch (Exception ex)

{

Report.AddError(“Unexpected exception has uccurred while finding web elements. Exception message: ” + ex.Message);

return null;

}

}

//if no element is found with all passed locators or an exception has been thrown, second attempt is performed after the pause specified in timeout parameter

continueSearch = !continueSearch;

if(continueSearch)

{

Report.AddInfo(“Waiting for ” + timeout + ” seconds”);

Thread.Sleep(timeout);

}

} while (continueSearch);

//if no element is found even after second attempt empty collection is returned

Report.AddInfo(“No” + type + ” ” + elementName + ” is found”, driver.TakeScreenshot(“Looking for ” + type + ” ” + name));

return elements;

}

GetWebElement()

The method is already created as a stub. At the moment it just return null, so let’s fill it with some logic. It is much simpler than previous one. The method invokes GetWebElements() method and if there is something in returned collection the first item is returned, otherwise – return null

public static IWebElement GetWebElement(this IwebDriver driver, Type elementType = null, string elementName = null, int? waitTimeout = null, params ElementLocator[] locators)

{

var elements = GetWebElements(driver, elementType, elementName, waitTimeout, locators);

if (elements == null || elements.Count == 0)

return null;

return elements[0];

}

The methods above are pretty slow but there is always the dilemma – to create rapid but unstable code or develop methods robust but slow. If you decide to use the methods or its parts you can make it quicker by removing reporting, additional verification, switching into frames, double search on fail, and so on.

Below are some other IWebDriver extension methods that are not obligatory but may be very helpful.

GetPageHashCode()

The method calculates and returns hash code of a page formatted as a string. It may be used for determination if any changes have occurred on a page since the last hash code calculation. For example, when IWebDriver clicks on a button on a page and we I not know exactly what concrete event will occur, with this method I can at least know that something has happened, that at least click action has been performed. The method is used inside Click() and DragAndDrop() methods of WebElement class.

/// <summary>

/// calculates hash code of a web page

/// </summary>

/// <param name=”driver”>instance of IWebDrive</param>

/// <returns>web page hash code formatted as a string</returns>

public static string GetPageHashCode(this IWebDriver driver)

{

MD5 md5 = MD5.Create();

byte[] bytesArray = Encoding.ASCII.GetBytes(driver.PageSource);

byte[] hashCode = md5.ComputeHash(bytesArray);

var sb = new StringBuilder();

foreach (byte t in hashCode)

sb.Append(t.ToString(“X2”));

return sb.ToString();

}

IsAlertPresent()

The method checks if an alert message is currently displayed in browser. The code is very simple.

/// <summary>

/// indicates if an alert message is displayed on a page

/// </summary>

/// <param name=”driver”>current instance of IWebDriver</param>

/// <returns></returns>

public static bool IsAlertPresent(this IWebDriver driver)

{

try

{

driver.SwitchTo().Alert();

return true;

}

catch

{

return false;

}

}

[row]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
WebElement [/column]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
Table Of Content
[/column]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
ClickableWebElement class
[/column]
[/row]

Leave a Reply