Code & QA

Custom classes for description of web elements instead of IWebElement interface

At the moment there is HomePage class in the framework which has just only constructor and IsLoaded property. The property of course may be used as is but you can notice that all elements used inside the property are of IWebElement type. I guess that these elements may be used outside the IsLoaded property and therefore they must be described one more time. It leads to duplication of code. That’s why I’m about to move description of the elements to separate properties and I will just use the properties inside IsLoaded property.

I do not want to describe any of page elements as IWebElement instance because the interface does not provide event logging and its usage may be pretty uncomfortable and complicated when seeking for an element requires a number of actions like switching to frames, parsing a table or jumping by tree-view. That’s why I created my own classes for representation of each kind of web element like buttons, textboxes, labels, radiobuttons, checkboxes, links, images and so on. All these classes are derived from WebElement and ClickableWebElement classes which I wrote about above.

This approach allows automatically adding log information to summary report about any event related to the element and it allows more elegant interaction with the elements methods and properties such as Text, Displayed, Click(), selected, read-only, etc. In addition it allows overriding some properties or behavior of a concrete web element.

P.S. On my opinion all locators of web elements should be stored together with description of the elements, in other words they must be embedded into the code. I saw other approaches when locators were put in separate files, were stored in databases and within some repositories. But the result was always the same – when number of elements’ locators becomes more than 200 or 300 the chaos comes. It turns incomprehensible to which ‘Search’ button the locator is related, a lot of locators of the same element appear with different names because one named a button ‘search’, another one named it as ‘buttonSearch’, someone else gave ‘searchButton’ name, and finally you do not know exactly which of the buttons to use and are all the buttons different elements. It will take a lot of time to maintain, update and to support all locators up-to-date.

WebImage

This class represents web elements usually described in HTML code with tag <img />. This is a picture which has its own address (src), tooltip (alt), general for all web elements attributes Displayed and Enabled; sometimes it may be implemented as a link, therefore, it has method Click(), and sometimes it may be available for moving (Drag and drop). In addition it has following auxiliary fields: Name – description of the element for usage in summary report, IsFound – for determination that the element is located on a page and may be interacted with.

There is also custom implementation of passing element’s locators and searching for the element inherited from abstract class ClicableWebElement, which inherits WebElement class.

The class has two constructors. The first receives IWebDriver and IWebElement instances as parameters if the element is already known as IWebElement.

The second constructer receives:

· IWebDriver driver – an instance of IWebDriver interface which interacts with the element. It is initialized in BaseTest.Setup() method and passed as a parameter to WebApplication class and then to page class. Obligatory parameter.

· Name j- element name. Used only for reporting.

· int? waitTimeout – period of time in seconds that indicates how much time is allocated to the driver for searching the element on a page. If the element is not found with specified locators, an exception will be thrown.

· ElementLocator[] locators – array of element’s locators. Usually an element may be found with a single locator, but sometimes an element may have a number of locators (for example, it may vary from a page settings). In such cases there may be passed two or more locators to constructor. The driver first tries to find the element with the first locator in the array and if it cannot find it tries to do it with the next.

I added new class named WebImage to WebElements folder.

using Logger;

using OpenQA.Selenium;

using System;

namespace Mapping.WebElements

{

public class WebImage : ClicableWebElement, IImage

{

public WebImage(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebImage(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

}

}

At the moment WebImage class has the following inherited fields and methods:

public string Name;

public string Tooltip;

public string Src;

public bool IsPresent;

public bool IsDisplayed;

public bool IsEnabled;

public void Click();

public void DragNDrop();

Most of them are already declared and implemented in parent classes. The only unimplemented are properties Tooltip and Src, declared in IImage interface. So in the class code I have only to implement these two properties. Below is the code.

public string Src

{

get

{

if (IsFound)

{

try

{

return Element.GetAttribute(“src”);

}

catch (Exception ex)

{

Report.AddWarning(“Getting image source address”, “Value of attribute ‘src’ is read”, “Getting web element attribute ‘src’ throws the exception: ” + ex.Message);

}

}

return string.Empty;

}

}

public string Tooltip

{

get

{

if (IsFound && IsDisplayed)

{

try

{

string tooltip = Element.GetAttribute(“alt”);

if(string.IsNullOrEmpty(tooltip))

tooltip = Element.GetAttribute(“title”);

return tooltip;

}

catch (Exception ex)

{

Report.AddWarning(“Getting image tooltip”, “Value of attribute ‘title’ is read”, “Getting web element attribute ‘title’ throws the exception: ” + ex.Message);

}

}

return string.Empty;

}

}

As I said the properties Name, IsPresent, IsDisplayed, IsEnabled and the methods Click(), DragAndDrop() are inherited from WebElement and ClicableWebElement classes.

Ok. For now WebImage class is completely done. Now I’ll try to create its instance.

Assume I want to check that default picture in Gallery section of tested application is correct one. Its source file is “DSC_0164.jpg” and its tooltip shows “Hungarian Parliament. View from 12th floor of hotel Budapest” text.

First thing I have to do is to add the picture’s description to appropriate page. I know that it is located on Gallery page. So, I have to create Gallery page class first.

clip_image002

I added simple constructor and simple implementation of IsLoaded property

using OpenQA.Selenium;

namespace Mapping.TestingWithSelenium

{

public class GalleryPage : MasterPage

{

public GalleryPage(IWebDriver driver) : base(driver) { }

public override bool IsLoaded

{

get

{

try

{

var masterPageIsLoaded = base.IsLoaded;

Driver.SwitchTo().Frame(“innerContent”);

var pic = Driver.FindElement(By.Id(“pic”));

return (masterPageIsLoaded && pic.Displayed);

}

catch { return false; }

}

}

}

}

Then I added private variable of WebImage type to the class and public property which returns the variable:

#region Elements

private WebImage picture;

public WebImage Picture

{

get

{

if (picture == null || !WebApplication.IsValid)

picture = new WebImage(Driver, “Gallery picture”, locators: new ElementLocator(new[] { “innerContent” }, By.Id(“pic”)));

return picture;

}

}

#endregion Elements

The property returns an instance of WebImage class. WebImage constructor takes three explicit parameters: instance of IWebDriver, name of the element (it is custom description for summary report), locator. I don’t expect the image being loaded in some special way, so I’m sure default timeout for its search is sufficient. That’s why waitTimeout is not passed – default value is used. It is currently null and it will be replaced with default value determined in App.config file when the driver will be looking for the element. It will be done inside Extensions.GetWebElement() method. I pass only one locator because I know it’s enough for the element being found. I also know that the element is inside a frame, therefore the locator is complex:

new ElementLocator(new[] { “innerContent” }, By.Id(“pic”))

The locator consists of two entities – an array of frames’ locators (object type) and element’s locator of By type. To get the element the driver should be switched to one frame depth – into ‘innerContent’ iframe. Therefore, the first parameter is passed as anonymous array of frame locators with only one item “innerContent” which is id of the iframe. It means that the driver first implicitly is switched to the top window, then to the ‘innerContent’ iframe, then it looks for a web element with id = ‘pic’.

if (picture == null || !WebApplication.IsValid)

part of code means that if picture variable is not initialized yet or if current page of the application is considered out of date (not valid any more) the picture will be sought on the page, recalculated and returned as new instance.

The image is described and ready for use. But before, I want to adjust IsLoaded property of the page. Currently the same element (gallery picture) is defined in the class twice – as Picture property and inside IsLoaded property as pic variable. To avoid the redundancy I replace pic variable with Picture property. So my IsLoaded property transforms to:

public override bool IsLoaded

{

get

{

try

{

return (base.IsLoaded && Picture.IsDisplayed);

}

catch { return false; }

}

}

Wrapping of simple picture with properties and methods guarantee that if anything goes wrong I’ll get an appropriate message in my summary report with full description and screenshot. I will also get info message if everything’s ok. In addition such approach let me be sure that the element is not recalculated redundantly, I do not have to care about waiting the element on a page, catching of exceptions, switching between frames. All the actions are done automatically in parent and auxiliary classes.

Now I can add the GalleryPage to the Application class and create a test.

using Logger;

using Mapping.TestingWithSelenium;

using OpenQA.Selenium;

using System.Configuration;

namespace Mapping

{

public class WebApplication

{

/ … /

#region Pages

private HomePage _homePage;

private GalleryPage _galleryPage;

public HomePage HomePage

{

get

{

if (_isNew)

Open<HomePage>();

if (!IsValid || _homePage == null)

{

_homePage = new HomePage(Driver) { Name = “Home” };

//Actually the variable must be set True, but if a page contains frames it may cause problems because some elements of a page may be located in a frame and others may be out. If you refer to an element out of the frame, then refer to an element inside it, you’ll get StaleElementException referring again to outer element. It occurs because the driver is switched inside the frame and everything out of it is not available any more. When IsValid is False it forces recalculating an element every time it is referred to.

IsValid = false;

}

return _homePage;

}

}

public GalleryPage GalleryPage

{

get

{

if (_isNew)

Open<GalleryPage>();

if (!IsValid || _galleryPage == null)

{

_galleryPage = new GalleryPage(Driver)

{

Name = “Gallery”

};

IsValid = true;

}

return _galleryPage;

}

}

#endregion Pages

public void Open<T>(params object[] args) where T : IWebPage

{

_isNew = false;

if (typeof(T) == typeof(HomePage))

{

Report.AddInfo(“Opening Home page”);

Driver.Navigate().GoToUrl(Globals.applicationURL);

}

else if (typeof(T) == typeof(GalleryPage))

{

Open<HomePage>();

Driver.FindElement(By.XPath(“.//a

[text language=”Gallery”][/text]

“)).Click();

}

}

/ … /

}

}

In the BaseTest class I initialize WebApplication instance – an instance of tested application. This instance is used for execution of all tests in the test collection (test class, fixture).

using Logger;

using Mapping;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using NUnit.Framework;

using OpenQA.Selenium;

using OpenQA.Selenium.Chrome;

using OpenQA.Selenium.Firefox;

using OpenQA.Selenium.IE;

using OpenQA.Selenium.Remote;

using System;

using System.Collections.Generic;

using System.Collections.Specialized;

using System.Configuration;

using System.Diagnostics;

using System.Globalization;

using System.IO;

using System.Threading;

using MSTest = Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Tests

{

public class BaseTest

{

protected IWebDriver driver;

protected WebApplication TestedApplication;

/ . . . /

[SetUp][TestInitialize]

public void Setup()

{

if (ConfigurationManager.AppSettings[“browserType”] == “Android”)

StartProcess(“cmd.exe”, “java -jar selendroid-standalone-0.15.0-with-dependencies.jar”);

driver = SetUpDriverInstance(ConfigurationManager.AppSettings[“browserType”]);

TestedApplication = new WebApplication(driver);

}

/ . . . /

}

}

Now I can rebuild the Mapping project and create a test. Remind the scenario: “I want to check that default picture in Gallery section of tested application is correct picture. Its source file is “DSC_0164.jpg” and its tooltip shows “Hungarian Parliament. View from 12th floor of hotel Budapest” text.”

namespace Tests

{

[TestFixture][TestClass]

public class TestingWithSelenium : BaseTest

{

[Test][TestMethod]

public void GalleryPicture()

{

var pictureAddress = “DSC_0164.jpg”;

var tooltip = “Hungarian Parliament. View from 12th floor of hotel Budapest”;

var testScope = new Dictionary<string, string> {{ “Verify Gallery page picture”, “Gallery page pricture address is ” + pictureAddress + ” and picture title is ” + tooltip }};

Report.StartTestCase(GetType().Name, MethodBase.GetCurrentMethod().Name, “Verification of Gallery page picture”, testScope);

Report.RunStep(“Step 1: Verify Gallery page”);

NUnit.Framework.Assert.IsTrue(TestedApplication.GalleryPage.Picture.Src.Split(‘/’).Last() == pictureAddress);

NUnit.Framework.Assert.IsTrue(TestedApplication.GalleryPage.Picture.Tooltip == tooltip);

Report.AddInfo(testScope.ElementAt(0).Key, testScope.ElementAt(0).Value, driver.TakeScreenshot(“Gallery page picture”));

}

}

}

Pay attention that I do not search for the image in the test, there is no code for verification of element presence, visibility, page loading, no exception handling and explicit logging of made actions. All I do is just referring to the element’s properties Src and Tooltip:

TestedApplication.GalleryPage.Picture.Src.Split(‘/’).Last()

TestedApplication.GalleryPage.Picture.Tooltip

All technical auxiliary information is added to the test summary automatically:

clip_image004

WebLabel

Label is one of simplest web elements. It represents a plain text on a page. In HTML code such elements may be formatted with tags <p>, <div>, <h1…h6>, <span> and others. This is simple class that is inherited from class WebElement (non-clickable) and has only one property Text, all the rest are implemented by the parent.

clip_image006

clip_image008

using Logger;

using OpenQA.Selenium;

namespace Mapping.WebElements

{

public class WebLabel : WebElement, ILabel

{

public WebLabel(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebLabel(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

public string Text

{

get

{

if (!IsFound)

Report.AddWarning(“Failed to get text value of ” + GetType() + Name, GetType() + Name + ” is displayed”, GetType() + Name + ” is not displayed”, Driver.TakeScreenshot(GetType() + Name + “is not displayed”));

return Element.Text;

}

}

}

}

Usage of WebLabel class

On the Gallery page there is a text which describes current picture and is represented with <label> tag. I want to verify that all descriptions match their pictures.

I added the label to the GalleryPage class.

#region Elements

private WebImage picture;

private WebLabel pictureTitle;

public WebImage Picture

{

get

{

if (picture == null || !WebApplication.IsValid)

picture = new WebImage(Driver, “Gallery picture”, locators: new ElementLocator(new[] { “innerContent” }, By.Id(“pic”)));

return picture;

}

}

public WebLabel PictureTitle

{

get

{

if (pictureTitle == null || !WebApplication.IsValid)

pictureTitle = new WebLabel(Driver, “Gallery picture title”, locators: new ElementLocator(new[] { “innerContent” }, By.Id(“title”)));

return pictureTitle;

}

}

#endregion Elements

Additional element of the Gallery page necessary for the test is the list of thumbnail images. I know exactly what picture must be loaded when certain thumbnail is clicked and I also know what description for every picture must be displayed. So, in the test I’m going to go through all the thumbnails and verify which picture and what description are shown.

using Mapping.WebElements;

using OpenQA.Selenium;

using System.Collections.Generic;

using System.Linq;

namespace Mapping.TestingWithSelenium

{

public class GalleryPage : MasterPage

{

/ . . . /

#region Elements

private WebImage picture;

private WebLabel pictureTitle;

private List<WebImage> pictureThumbnails = new List<WebImage>();

/ . . . /

public List<WebImage> PictureThumbnails

{

get

{

if (pictureThumbnails.Count == 0 || !WebApplication.IsValid)

{

var collection = Driver.GetWebElements(typeof(WebImage), “picture thumbnail”, locators: new ElementLocator(new[] { “innerContent” }, By.XPath(“.//tr/td[1]/img”)));

for (int i = 0; i < collection.Count; i++)

pictureThumbnails.Add(new WebImage(Driver, “picture thumbnail “ + (i + 1), collection.ElementAt(i)));

}

return pictureThumbnails;

}

}

#endregion Elements

}

}

After the Mapping project is rebuild the test may be designed as following:

[Test][TestMethod]

public void GalleryPictureLabel()

{

var testData = new Dictionary<string, string> {

{“DSC_0164.jpg”,”Hungarian Parliament. View from 12th floor of hotel Budapest”},

{“DSC_0470.jpg”,”Padua. Central square”},

{“DSC_0519.jpg”,”Antient Roma”},

{“DSC_0832.jpg”,”Vatican Museum. Sphere Within Sphere”},

{“DSC_0862.jpg”,”Vatican. St. Peter’s square”},

{“DSC_0948.jpg”,”City of Venice. Police”},

{“DSC_1051.jpg”,”City of Venice. Fire department”},

{“DSC_0979.jpg”,”City of Venice. Ambulance visiting a patient”}};

int i = 0;

var testScope = new Dictionary<string, string> { { “Verify Gallery page pictures descriptions”, “All Gallery page prictures descriptions are correct” } };

Report.StartTestCase(GetType().Name, MethodBase.GetCurrentMethod().Name, “Verification of Gallery page pictures descriptions”, testScope);

Report.RunStep(“Verifying picture and picture description”);

foreach (var thumbnail in TestedApplication.GalleryPage.PictureThumbnails)

{

Report.AddInfo(“Verifying picture and picture description related to ‘” + thumbnail.Tooltip + “‘ thumbnail”);

thumbnail.Click();

nUnit.Assert.IsTrue(TestedApplication.GalleryPage.Picture.Src.Split(‘/’).Last() == testData.Keys.ElementAt(i));

nUnit.Assert.IsTrue(TestedApplication.GalleryPage.PictureTitle.Text == testData.Values.ElementAt(i));

Report.AddInfo(testScope.ElementAt(0).Key, “Picture description is ‘” + testData.Values.ElementAt(i) + “‘”, driver.TakeScreenshot(“Gallery page picture description”));

i++;

}

}

WebContainer

Pretty simple element WebContainer is a block web element. It may be div or iframe or any element that contains other elements inside. It cannot be interacted but only getting its properties Displayed and Enabled. The element is inherited from WebElement class and does not implement any additional interfaces.

using OpenQA.Selenium;

namespace Mapping.WebElements

{

public class WebContainer : WebElement

{

public WebContainer(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebContainer(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

}

}

WebLink

WebLink class represents web elements usually represented in HTML code with tag <а />. This is a link to other web resources like web pages, files, scripts, etc. Generally any link has:

· href attribute – an address of web resource which the link is referred to;

· title attribute – displayed as a tooltip when the cursor is over the link.

WebLink class is designed similarly to WebImage class mentioned above. It has common for all web elements properties IsDisplayed and IsFound, as well as methods of clickable elements Click() and DragAndDrop(). Difference between WebLink and WebImage is only that WebLink has Href property and WebImage has Src one. Also WebLink has Text property and its Tooltip is implemented in different way. I do not see any sense in detailed describing of the class since it is very resembling to WebImage. Here is the code:

using Logger;

using OpenQA.Selenium;

using System;

namespace Mapping.WebElements

{

public class WebLink : ClicableWebElement, ILink

{

public WebLink(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebLink(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

public string Text

{

get

{

if (!IsFound)

Report.AddWarning(“Failed to get text value of ” + GetType() + Name, GetType() + Name + ” is displayed”, GetType() + Name + ” is not displayed”, Driver.TakeScreenshot(GetType() + Name + “is not found”)); var text = Element.Text;

if (!string.IsNullOrEmpty(text))

return text;

try

{

text = Element.GetAttribute(“value”);

}

catch

{

Report.AddWarning(“Failed to get attribute value of ” + GetType() + Name, string.Empty, string.Empty, Driver.TakeScreenshot(GetType() + Name + “is not found”));

}

return text;

}

}

public string Tooltip

{

get

{

if (IsFound && IsDisplayed)

{

try

{

var tooltip = Element.GetAttribute(“title”);

return tooltip;

}

catch (Exception ex)

{

Report.AddWarning(“Getting link tooltip”, “Value of attribute ‘title’ is read”, “Getting web element attribute ‘title’ throws the exception: ” + ex.Message);

}

}

return string.Empty;

}

}

public string Href

{

get

{

if (IsFound && IsDisplayed)

{

try

{

var href = Element.GetAttribute(“href”);

return href;

}

catch (Exception ex)

{

Report.AddWarning(“Getting link tooltip”, “Value of attribute ‘href’ is read”, “Getting web element attribute ‘href’ throws the exception: ” + ex.Message);

}

}

return string.Empty;

}

}

}

}

Below is the test with WebLink class in use. I click ‘Learn Selenium Testing’ link on Home page and verify that the link leads to correct address. Then I close Learn Selenium Testing blog and check that Home page is still displayed.

1. I added Learn Selenium Testing link to Home page class:

#region Elements

private WebLink _linkLearnSeleniumTesting;

public WebLink LinkLearnSeleniumTesting

{

get

{

if (_linkLearnSeleniumTesting == null || !WebApplication.IsValid)

{

_linkLearnSeleniumTesting = new WebLink(Driver, “Learn Selenium Testing”, locators: new ElementLocator(new[] { “innerContent” }, By.CssSelector(“#container a”)));

}

return _linkLearnSeleniumTesting;

}

}

#endregion Elements

clip_image010

2. I added Close() method to WebApplication class. The method closes current tab or window and switches the driver to previous tab (or it closes the browser if the only one tab has been opened).

/// <summary>

/// Closes current window and switches to previous (if exist). Closes browser if only one tab has been opened

/// </summary>

public void Close()

{

if (Driver.WindowHandles.Count > 1)

{

int currentWindowIndex = 0;

for (int i = 0; i < Driver.WindowHandles.Count; i++)

{

if (Driver.WindowHandles[i] == Driver.CurrentWindowHandle)

{

currentWindowIndex = i;

break;

}

}

var previousWindow = currentWindowIndex == 0 ? Driver.WindowHandles[Driver.WindowHandles.Count – 1] : Driver.WindowHandles[currentWindowIndex – 1];

Report.AddInfo(“Closing current browser window”);

Driver.Close();

Report.AddInfo(“Switching to previous browser windows”);

Driver.SwitchTo().Window(previousWindow);

}

else

{

Quit();

}

}

3. Then I created the test (after the Mapping project rebuilding)

[Test][TestMethod]

public void LinkLearnSeleniiumTesting()

{

string expectedUrl = “learnseleniumtesting.com”;

var testScope = new Dictionary<string, string> {

{ “Click link Learn Selenium Testing on Home page”, “learnseleniumtesting.com resource is navigated” },

{ “Close Learn Selenium Testing page”, “Home page is still displayed” }};

Report.StartTestCase(GetType().Name, MethodBase.GetCurrentMethod().Name, “Verification of WebLink, closing of current tab and switching to previous tab”, testScope);

Report.RunStep(“Step 1: Click link Learn Selenium Testing on Home page”);

TestedApplication.HomePage.LearnSeleniumTestingLink.Click();

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

wait.Until(ExpectedConditions.UrlMatches(expectedUrl));

Report.RunStep(“Step 2: Close tab”);

TestedApplication.Close();

nUnit.Assert.IsTrue(TestedApplication.HomePage.IsLoaded);

}

WebButton

Simple button. It may represent any web element you consider to be a button. Actually it is the same as WebLink but without href attribute; usually there is onClick() event assigned for buttons. So, the code of WebButton is almost similar to WebLink’s one. Moreover, you can inherit WebLink from WebButton and implement there only Href property.

WebButton.cs:

using Logger;

using OpenQA.Selenium;

using System;

namespace Mapping.WebElements

{

public class WebButton : ClicableWebElement, IButton

{

public WebButton(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebButton(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

public string Text

{

get

{

if (!IsFound)

Report.AddWarning(“Failed to get text value of ” + GetType() + Name, GetType() + Name + ” is displayed”, GetType() + Name + ” is not displayed”, Driver.TakeScreenshot(GetType() + Name + “is not found”));

var text = Element.Text;

if (!string.IsNullOrEmpty(text))

return text;

try

{

text = Element.GetAttribute(“value”);

}

catch

{

return string.Empty;

}

return text;

}

}

public string Tooltip

{

get

{

if (IsFound && IsDisplayed)

{

try

{

var tooltip = Element.GetAttribute(“title”);

return tooltip;

}

catch (Exception ex)

{

Report.AddWarning(“Getting element tooltip”, “Value of attribute ‘title’ is read”, “Getting web element attribute ‘title’ throws the exception: ” + ex.Message);

}

}

return string.Empty;

}

}

}

}

Updated WebLink.cs:

using Logger;

using OpenQA.Selenium;

using System;

namespace Mapping.WebElements

{

public class WebLink : WebButton, ILink

{

public WebLink(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebLink(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

public string Href

{

get

{

if (IsFound && IsDisplayed)

{

try

{

var href = Element.GetAttribute(“href”);

return href;

}

catch (Exception ex)

{

Report.AddWarning(“Getting link tooltip”, “Value of attribute ‘href’ is read”, “Getting web element attribute ‘href’ throws the exception: ” + ex.Message);

}

}

return string.Empty;

}

}

}

}

WebCheckbox

This web element is simple checkbox usually represented with HTML tag <input type=’checkbox’/>. The element actually may be clicked but it is not ClickableWebElement (common WebElement instead) and ICheckbox does not inherit IClickableElement interface as well. Therefore the WebCheckbox does not have methods Click() and DragAndDrop(). To make it available for interaction I added methods Select() and Deselect() instead of the Click(). But I still use the Click() for performing clicking (selecting or deselecting). Pretty complicated at first glance but I think this way is rather elegant than having Click() method for this type of elements. Because doing just clicking you must explicitly control the state of the element and obviously you can mistakenly click on already selected element when you want to select it and the element will become deselected instead. In the code below I added detailed explanation of the way I implemented Select\Deselect methods.

using System;

using Logger;

using OpenQA.Selenium;

namespace Mapping.WebElements

{

public class WebCheckbox : WebElement, ICheckbox

{

public WebCheckbox(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebCheckbox(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

public string Tooltip

{

get

{

if (IsFound && IsDisplayed)

{

try

{

var tooltip = Element.GetAttribute(“title”);

return tooltip;

}

catch (Exception ex)

{

Report.AddWarning(“Getting element tooltip”, “Value of attribute ‘title’ is read”, “Getting web element attribute ‘title’ throws the exception: ” + ex.Message);

}

}

return string.Empty;

}

}

public bool IsSelected

{

get

{

if (!IsDisplayed)

{

Report.AddWarning(“Checkbox ” + Name + ” is not displayed”, screenshotPath: Driver.TakeScreenshot(“Checkbox ” + Name + ” is not displayed”));

return false;

}

try

{

return Element.Selected;

}

catch (Exception ex)

{

Report.AddWarning(“Getting checbox selected property threw the following exception: ” + ex.Message);

}

return false;

}

}

public void Select()

{

if (!IsSelected)

{

//private button is created with the same parameters as this element is. The button has Click() method and the method is used for clicking the checkbox.

//So, btn as well as this element are referred to the same element on a page. Is like different representations of the same object.

WebButton btn = new WebButton(Driver, Name, Element);

//btn is clicked with only sending Spacebar key code to the element. Similar as you manually put the element in the focus and hit space bar

btn.ClickAction = new[] { Mapping.ClickAction.SendKeySpacebar };

//Click action is considered done only whet the element is selected. Note that Click() mathos is related to btn element and IsSelected is related to this element, but indeed btn and this are the same element, so the actions are correct

btn.ClickValidationAction = () => IsSelected;

btn.Click();

}

}

public void Deselect()

{

if (IsSelected)

{

WebButton btn = new WebButton(Driver, Name, Element);

btn.ClickAction = new[] { Mapping.ClickAction.SendKeySpacebar };

btn.ClickValidationAction = () => !IsSelected;

btn.Click();

}

}

}

}

Using this approach I can have checkboxes with Select() and Deselect() methods only which use unavailable for WebCheckbox type elements method Click() behind the scene.

clip_image012

WebTextbox

The class is created for representation of web elements which are text areas. Usually they are represented on a page with tag <input type=’text’/>, sometimes with <textarea />. The class is not ClickableWebElement but WebElement instead, therefore, it doesn’t have methods Click() and DragAndDrop() as well as accompanying fields and properties. Instead, it has its own specific methods Clear(), AppendText(), TypeText() and internal field-delegate BeforeTypingAction. It also inherits fields from WebElement class – Name, Text, IsPresent, IsDisplayed, IsEnabled, general for all web elements.

BeforeTypingAction is a delegate to a method of type System.Action (a method with a signature void MethodName()). If the delegate is initialized, it will be invoked right before a text is typed into the text field.

Method Clear(). This method removes everything from the text field making it empty.

AppendText(string text) appends parameter text to the text contained in the field.

TypeText(string text) clears the text field and then type text parameter into it.

using Logger;

using OpenQA.Selenium;

using System;

using System.Threading;

namespace Mapping.WebElements

{

public class WebTextbox : WebElement, ITextbox

{

public WebTextbox(IWebDriver driver, string name, IWebElement element) : base(driver, name, element) { }

public WebTextbox(IWebDriver driver, string name, int? waitTimeout = null, params ElementLocator[] locators) : base(driver, name, waitTimeout, locators) { }

internal Action BeforeTypingAction = null;

public string Text

{

get

{

if (!IsFound)

Report.AddWarning(“Failed to get text value of ” + GetType() + Name, GetType() + Name + ” is displayed”, GetType() + Name + ” is not displayed”, Driver.TakeScreenshot(GetType() + Name + “is null”));

string text = Element.Text;

if (!string.IsNullOrEmpty(text))

return text;

try

{

text = Element.GetAttribute(“value”);

}

catch

{

return string.Empty;

}

return text;

}

}

public void Clear()

{

Element.Clear();

}

public void AppendText(string text)

{

string initialText = Text;

string newText = initialText + text;

if (BeforeTypingAction != null)

{

Report.AddInfo(“BeforeTypingAction method is invoked”, string.Empty, string.Empty);

BeforeTypingAction.Invoke();

}

Element.Clear();

Element.SendKeys(newText);

if (Text.Equals(text))

{

Report.AddInfo(“Type text into ” + GetType().Name + ” ” + Name, “Inner element’s text value is equal to expected, Text value: ” + Text + “, expected value: ” + newText, Driver.TakeScreenshot(“Type text into ” + GetType().Name + ” ” + Name));

return;

}

Report.AddInfo(“Inserting text into ” + GetType().Name + ” ” + Name + ” using JavaScript”, string.Empty, string.Empty);

((IJavaScriptExecutor)Driver).ExecuteScript(“arguments[0].value = arguments[1]”, Element, newText);

Thread.Sleep(Globals.timeoutBetweenClicks);

if (Text.Equals(newText))

{

Report.AddInfo(“Type text into ” + GetType().Name + ” ” + Name, “Inner element’s text value is equal to expected, Text value: ” + Text + “, expected value: ” + text, Driver.TakeScreenshot(“Type text into ” + GetType().Name + ” ” + Name));

return;

}

Report.AddWarning(“Type text into ” + GetType().Name + ” ” + Name, “Inner element’s text value is not equal to expected”, “Text value: ” + Text + “, expected value: ” + text, Driver.TakeScreenshot(“Type text into ” + GetType().Name + ” ” + Name));

}

public void TypeText(string text)

{

Clear();

AppendText(text);

}

}

}

[row]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
Classes of pages. IsLoaded property [/column]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
Table Of Content
[/column]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
Test with the web elements
[/column]
[/row]

Leave a Reply