Converting my existing code to PageObject design pattern with PageFactory - webdriver

I'm creating tests using Selenium 2 Web Driver with C#.Net. After reading through a lot of the Selenium documentation, I am not sure if I'm followign the correct design pattern and feeling unsure on how to go about testing using PageObject design patterns.
here is my current code that I'm using on my page and its working
WaitForElement(By.CssSelector("input#ctl00_ctl00_signinControl_txtUsername")).SendKeys("abc123");
WaitForElement(By.CssSelector("input#ctl00_ctl00_signinControl_txtPassword")).SendKeys("password");
SelectElement select;
IWebElement selElement = WaitForElement(By.CssSelector("select#ctl00_ctl00_ddlGoTo"));
select = new SelectElement(selElement);
select.SelectByText("Homepage");
*<more code .....>*
also I have told that I can not use Select page element using pageFactory.
Do I need to change my code the way I have coded? any feedback would be great.

The idea of the page object pattern is to have an object that represents the page. You are essentially writing an API for how to interact with the page.
For example a login page object may have the following methods:
enterUserName(String userName);
enterPassword(String password);
clickLoginButton();
The person using the page object to interact with the page does not need to care about how selenium finds elements and interacts with them. If the id on a field changes you would just need to change the locator on the page object and would not need to change all tests that call the associated page object public method.

Related

How to get the generated Unique ID prefix for a page's controls

As we know, ASP.NET WebForms will generate a Unique ID (as well as name) to a control to prevent collisions in the control heirarchy. Let's say we have a TextBox control with an assigned ID of "MyTextBox" in the markup. If this textbox is on a page with a Master Page then the TextBox control will be given a Unique ID of "ctl00$MainContent$MyTextBox" or something similar.
What I want to know is, for a given page, is it possible to know what the prefix WILL BE? In my above example I would like to know all controls I create on that page will be assigned with a prefix of "ctl00$MainContent$". I have examined the Page object and I cannot find an easy way to extract this information. Note: inspecting already existing controls on the page (like the TextBox) isn't an option. I simply need to know, at run time, what the prefix would be.
-- EDIT: Why do I need to do this? --
Ultimately I am trying to solve the problem that this post illustrates:
ASP.NET 4.5 TryUpdateModel not picking Form values in WebForm using Master-Page
I'm using the ModelBinding features introduced in ASP.NET 4.5. Problem is, as the above post points out, is that the name value collection found in the form will NOT match up with your model's properties. The built-in FormValueProvider expects a one-to-one match with the form key (name) and the model's properties. Unfortuantely, the form's keys will have the typical "ctl00$MainContent$" prefix to the names.
I have a semi-working solution where I created a custom IValueProvider that compares the end of the form key with the model's property. This works 95% of the time, but there's always a chance of multiple hits.
Ideally, and this is what I'm trying to figure out, if I could determine WHAT the prefix is I can then prefix that the IValueProvider's passed in key, look for that in the form and find the exact match.
So that is why I'm wondering if there's any way to know what the prefix should be for a given page.
The real answer is to simply code in such a way that you never have to know this information. that might not always be easy - but that's quite much what we do. You can certainly in code behind get/grab the "id" of the given button.
so for example, I become VERY tired of having to wire up a little toast message all over the place. So, I put in a little js routine (in the master page).
But I did need the client ID of a given control.
Well, the code behind needed (wants) to place the toast message next to whatever I clicked on.
So my server side "toast" message caller of course will after the server side code is done does the common "script" inject that will run when the page finally makes it final trip back down to the browser, displays the page, and then of course runs that script I injected.
So, my server side code does this:
MyToast2(Me, btnUpdate.ClientID.ToString, "Update ok!", "Settings changed")
So note how I get/grab/pass the "ID" of the control that the server is going to render. You can use ClientID to get the the final "ID" used for that control in code behind.
So, that btnUpdate is just a simple button placed on the web form. But who cares what super ugly "ID" the server assigns. i just need the "id" of the control so the JavaScript client side can pick up that control - and thus know/get/have the position of the control, and thus I get this result:
Or if I am some place else - again I can call that js routine - and that routine needs the current control. so might have this:
So, I can now just call a routine and pop up a message - not have to write any new js code for the gallzion notices and little pops I have all over the place.
so the little javaScript routine of course does this:
function toastcallm(cntrol, h, t, d) {
var cmd = $('#' + cntrol);
var mypos = cmd.position();
bla bla bla
But only important was that I get/determine and pass the used server "client" id to that routine - I don't really care what it is , or how to see, or how to list them out. I suppose a better jQuery selector or using wild card might work - but I really don't want to know the control ahead of time - but only that I can get the clientID used when I need it.
I simply write code that assumes somewhere along the way when I need such a client id, I simply get it and use it.
So, on the server side? Well, we always build and write code based on the control ID, but you want to get your hands on the actual id? Then you can use in the server code behind:
btnUpdate.ClientID.ToString
(ie: somecontrol.ClientID).

Dynamic element id's in WebDriver

Recently I started learning WebDriver as my client that I am working for is planning to use WebDriver for automating web applications.
I have doubts regarding how WebDriver locates the elements on webpage whose id's are dynamically changing (like changing for every login to application). Can anyone explain how we can accomplish this task with WebDriver?
Locating elements with dynamic id's can be fragile. I would rather use some visible text with for example xpath expression. My point is that in most cases the visible text is usually part of the requirements or specification of the application and id's are not. Therefore the id's are more likely to change and visible text not so.
For example to locate the username field in login form I might use xpath:
//label[.='Username']//following::input[1]
This is assuming there is a label "Username" before the input field.
I have found Firebug console function $x("xpath string") to be very useful when debugging those xpaths.
For those elements on the webpage whose ids are dynamically changing:
You can try locating the elements by their Xpath locator or CSS locator
You can find more information about the locator strategies that can be employed while using WebDriver here . Have a look at these and you would figure out the various locator strategies.
In order to understand the concept for locating dynamic elements you can have a look at the Selenium1 documents here. However pls note the api in this link is for Selenium 1. But you can use the concept and the locator strategy/api provided for the WebDriver earlier to accomplish your task
We had the same problem and we ended up using jquery selectors, especially if jquery is already available at your client-side. In the ZK framework that we use, we already have some jquery extensions so we could simply write:
assertEquals(driver.findElement(By.jq("#label:eq(0)")).getText(),"ROOT_MESSAGE");
where as the By.jq() effectively boils down to :
return (WebElement)((JavascriptExecutor)context).executeScript("return jq('" + selector + "').get(0);");
You can achieve it by using contains() method which is matched element name.
WebElement cls = (new WebDriverWait(ff, 10))
.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[#class='_54nc']/span/
span[contains(text(), 'Log Out')]")));
You can try:
https://github.com/sdl/Testy/
syntax is preaty simple:
// 1. import maven dependency
// 2. init Framework in your TestBase after initializing your driver
WebDriverConfig.init(driver);
// 3. any actions based on many many attributes
WebLocator logoutBtn = new WebLocator().setText("Log Out");
// make sure the element is rendered even after fiew seconds (5 by default)
logoutBtn.assertReady();
// or do any actions with elements
logoutBtn.click();
// more properties for selecting/testing specific element with wanted attributes
WebLocator closeIcon = new WebLocator().setClasses("close").setTitle("Close");
WebLocator minimIcon = new WebLocator().setClasses("minimize").setTitle("Minimize");

Using and call bookamrk in activities using rehosted

I'm using WF, I res-hosted the designer, and everything is fine till i need in my custom activities to wait a value when workflow is running, that what i made using BOOKMARK. i tried bookmark in ConsoleApplication and its work.
the following code i used in the main class in the ConsoleApplication:
AutoResetEvent syncEvent = new AutoResetEvent(false);
wa.Completed = delegate(WorkflowApplicationCompletedEventArgs r)
{
syncEvent.Set();
};
wa.Run();
wa.ResumeBookmark("bookmarkName", Console.ReadLine());
syncEvent.WaitOne();
but here in my rehosted project, i need to call many workflows. not only 1. and I can't specify it before runtime.
for this method:
wa.ResumeBookmark("bookmarkName", Console.ReadLine());
1st argument: bookmarks' names, i tried and i found i can name all bookmarks the same name.
About the 2nd argument, how can I pass the value for each workflow, and some workflows have more than 1 bookmark.
I have to read the value from many places, asp.net pages, DB and others.
Im sure there is something like these in WF 4.0, but i didnt find it. can u help me please :)
Thank you.
You need to keep track of each workflow's Id, and record that Id when a bookmark is created. Once you wish to return to the workflow, you can search for the one with the matching Id and then resume your bookmark.

ASP.NET options/command generator framework?

I want to put context-sensitive, dynamic command options on my asp.net pages.
I tried coding my own command structure but it's not very good, and I'm sure there must be a framework for doing this somewhere I can re-use?
Example:
I have a detailsview for some database object, I want to code in the object class what commands are available, based on the state of the object. I then want a UI object I can place on the webform that will pass commands back to the object when user clicks them, or jump to a different link (e.g. when additional parameters are available).
e.g. form might look like this
Product Details
Name: XXXX product
Price: $1.00
Qty: 1
Commands:
> Edit
> New Stock
> Mark as obsolete
So the commands at the bottom would have very little UI code and pass actions back to the object. For example the New Stock command would jump to a new page to ask for a quantity.
I don't know of the framework, but you could create something yourself. Let's say you are using MVP pattern, and assuming that this is a CRUD application, you could tell each view what type of object it is related to, then annotate you object with operations that are available. Then Presenter could call Service to perform the operation. You could name your methods using some convention so that you can wire it up in a Service. It is a lot of work, and unless you have 100s of views it is not worth while. I am building app that is about that size, and I am in process of creating GenericMVP framework, that would make wiring a breeze.

Is there any way to use XmlSiteMapProvider within WinForm/Console/VSTest application?

I wonder whether there is a workaround for using the standard XmlSiteMapProvider within a non asp.net application, like WinForm/Console or, in my case, VS Unit Test one.
The following code fails, because it cannot create a path to the .sitemap file inside a private GetConfigDocument method.
XmlSiteMapProvider provider = new XmlSiteMapProvider();
NameValueCollection providerAttributes = new NameValueCollection();
providerAttributes.Add("siteMapFile", "Web.sitemap");
provider.Initialize("XmlSiteMapReader", providerAttributes);
provider.BuildSiteMap();
I feel the right solution is to write another provider.
I do not see why not. It is just a provider that implements an interface. You may not need many of the features, but you can access the API for what it provides you. Your WinForms screens can simply use the Urls for identification so that you can determine your place in the hierarchy.
What you may have to do is create a custom implementation of the provider because it will use the HttpContext to get the Url of the current web request to identify current placement while you will need to get that value differently. That is what could be tricky because your WinForm application could be displaying multiple windows at time. If you know there is only one window showing at a time you could use a static value which is set prior to accessing the SiteMap API.
Now you have to question the value of using an API if you have to do all of the work. There may not be enough benefit to make it worthwhile.

Resources