In my effort of trying to find a neat way to validate which buttons are visible to the user based on his Role and Permissions i have encountered a problem. I am using the Page Object Model design pattern with selenium to test a website. A few pages have a different set of buttons which should either be visible or not to the user according to his role.
I keep a Dictionary<Permission,IWebElement> and initialize it in the constructor of the page (class representing a certain page in the site).
All web elements are defined as follows:
private IWebElement btn_openShop => driver.ById("open_shop");
(ById is equivalent to FindsElement(By.Id("open_shop"))
The problem is that if the button shouldn't exist an exception is thrown when adding it to the Dictionary.
Note: moving the initialization of the Dictionary wont help since i test both cases (one in which the user should see the button and one in which he shouldn't).
I changed the Dictionary to be of type <Permission,Lazy<IWebElement>>
and added items to it as follows:
dictionary.Add(somePermission,new Lazy<IWebElement>(()=>the button))
Edit: this technique works but it seems that when debugging in visual studio the code crashes.
Any thoughts? (no exceptions are thrown during test execution).
Related
I am using Selenium Page factory and I want to make certain extensions to it, but can't as they are sealed, so I want to write my own custom logic.
Question :
How does PageFactory.InitElements work so that all the properties loads their values when they are getting used and not when this method is called..
So, To Explain it with an example
//// Button on page load
[FindsBy(How = How.CssSelector, Using = "#lst-ib")]
public IWebElement Btn;
//// Button redirecting to Page 2
[FindsBy(How = How.CssSelector, Using = "#lst-ib")]
public IWebElement LinkBtn;
////Button on second page
[FindsBy(How=How.CssSelector, Using = "#rso > div:nth-child(1) > div > div:nth-child(1) > div > div > h3 > a")]
public IWebElement NewBtn;
So, the beauty of their page factory is that all the elements are loaded but they convert to webelements when they are in use, beacause if all the properties are assigned values on initilisation, NewBtn Property would always fail, as it is on page 2 .
So, what concept they might be using of initializing properties so they are assigned on usage and not at the run time , any dummy code would be great and appreciated to understand
Thanks in Advance
I have got absolutely no experience in C# but I checked the source code of the C# selenium implementation and seems pretty much identical to the Java code.
PageFactory.cs - This class provides the overall framework of how the elements are initialized.
The initElements() gets all the fields in the pageobject. For each field gets the annotation on them. Then it creates a Proxy for each field.
Then it stores the actual locator to be used. The actual call to findElement or findElements is inside the Invoke method of the proxy.
This is all handled by two classes - DefaultPageObjectMemberDecorator.cs and DefaultElementLocator.cs. There are two proxies which handle invocations - WebElementProxy.cs and WebElementListProxy.cs
So the custom logic that you want to write could be done by creating new classes which implement the appropriate interfaces. Then pass these new classes into the appropriate initElement() method of PageFactory class.
For a detailed understanding look at the Decorate() method of DefaultPageObjectMemberDecorator.cs class.
When creating the page object, the "page object logic" (IMO, from my experience and what I have understood so far from c# and selenium) it expects all the elements mentioned in an element map to exist in the DOM.
If your NewBtn exists in the dome but is hidden (and becomes visible when navigating to second page, basically when the page does not get refreshed and there is some ajax to it), then it stands to reason that the element map won't have any problem handling the element. You just have to assure that the element is visible with an IF statement or add a wait (to see if you are indeed to the second page).
If the page gets refreshed when navigating to the second page, then simply re-initialize your page object with new MyPageObject() so that all elements get mapped again, to avoid StaleElement and/or ElementNotFound exceptions etc.
Here is a little background on the specifications of my project:
We use Specflow and Microsoft CodedUI Framework for UI Automation
I have built a PageFactory that combines three Abstract Base Classes : BasePage, BaseMap, and BaseValidator that all Maps, Pages, and Validators inherit
Our Application that we are automating has numerous workflows that make defined HTML Controls have different InnerText Values (HTMLComboBoxes for example)
Everything is and needs to be abstracted from the actual Specflow Test Code in the Page Object Pattern, no unique code can exist within a Specflow Step
In my Maps I have certain controls like a combobox that has an InnerText change if a certain workflow is selected. I need to build assertion and verification statements to make sure the InnerText is correct for the workflow that is selected. This is not a problem. However, I do not want to just define a new variable for every InnerText change(There are A LOT).
Is there any way I can account for the InnerText variations in the Page Object Pattern and not have to code a new variable for every single one?
Here is an example of a Map Entry:
public HtmlComboBox NextActionControlDropDownList()
{
var NextActionControlDropDownList = new PropertyExpressionCollection {
new PropertyExpression(HtmlComboBox.PropertyNames.Id, "MEDCHARTContent_EmmpsContent_nextActionControl_ActionDropDownList", PropertyExpressionOperator.EqualTo)
};
return Window.Find<HtmlComboBox>(NextActionControlDropDownList);
}
This is the Base Control definition. It can also be this:
public HtmlComboBox NextActionControlARFormalComplReview()
{
var NextActionControlARFormalComplReview = new PropertyExpressionCollection {
new PropertyExpression(HtmlComboBox.PropertyNames.Id, "MEDCHARTContent_EmmpsContent_nextActionControl_ActionDropDownList", PropertyExpressionOperator.EqualTo),
new PropertyExpression(HtmlComboBox.PropertyNames.InnerText, "--Select Action-- Return to USARC ", PropertyExpressionOperator.EqualTo)
};
return Window.Find<HtmlComboBox>(NextActionControlARFormalComplReview);
}
My thoughts so far were to maybe make another map and inherit it? But that wouldn't solve my initial problem of too many variables for a single control. I don't see how If statements would help either because it needs to be defined for the framework to find the control. Maybe I could store the differing values in a collection of sorts and have a parameter key value to access them... but that seems like I would run into a lot of issues.
If you try and see the methods under PropertyExpressionOperator you would see something called Contains.
new PropertyExpression(HtmlComboBox.PropertyNames.InnerText, "--Select Action--", PropertyExpressionOperator.Contains)
I'm trying to do something my teacher says can't be done; I would like to prove him wrong.
In the CreateChildControls method of my SharePoint 2010 webpart, I am referencing a User Control file called "ChartUserControl.ascx" in my project that contains the ASP.NET code for a WebChartControl object configured just the way I want it. WebChartControl has an ID of "OrderQtyChart".
What I want to do is take the code from that UserControl and use it create a new WebChartControl, called "chart", with matching configuration. I'm trying to do this because there are callbacks etc. that need to be performed on the chart after it's created to actually populate it with chart-stuff.
So, my code:
WebChartControl chart;
protected override void CreateChildControls()
{
ChartUserControl userControl = new ChartUserControl();
// referencing file ChartUserControl.ascx as an object
chart = userControl.FindControl("OrderQtyChart") as WebChartControl;
// or
chart = (WebChartControl)userControl.FindControl("OrderQtyChart");
// Trying to tell the code to create 'chart' using the code defined in object
"OrderQtyChart" located in ChartUserControl.ascx
}
Or something like that. In either instance above, 'chart' will return null.
I'm trying to use the front end code of OrderQtyChart as a template for 'chart'; they're both the same type of object and I don't get any errors until I try to create 'chart' on my page, at which point I'm told it's null.
Is there a way to do this? It would save me a ton of time not to have to configure 'chart' completely at creation time. Even if I have to reference my front-end code for OrderQtyChart a different way.
Thanks.
[Edited 7/9 for clarity]
What you are trying to do seems very well possible and I assume your teacher did not understand your question correctly. Here are a few tips on how this is done:
Object A could be one of these:
A visual control such as label or textbox. In this case your will have to traverse the visual controls from parent to child by doing direct parent.FindControl("ObjectA");
It is an instance of a class. This might be a MyClass or a new textbox that is created by code. In this case you'll have to create a public property that has a getter which returns ObjectA. although you can use FindControl in case ObjectA is a UI component that is created and added dynamically at run-time. Otherwise, you'll have to stick with property.
FindControl will not traverse the parent to child hierarchy, so you'll have to do a recursive method in order to successfully find the ObjectA or if you have access to its direct parent, call FindControl on that. More info here: http://geekswithblogs.net/QuandaryPhase/archive/2009/05/06/asp.net-recursive-findcontrol-amp-extension-methods.aspx
Page life cycle plays an important role here, so make sure that you keep it in mind or you'll end up with a null reference that is not really caused by FindControl
Gah, never mind. I realized I can just call the user control directly and I'm seriously overcomplicating this.
That's a whole new question, so I'll just start a different thread.
One application I'm automating some tests for is initializing a Text field (UIA_TextControlTypeId(0xC364)) and the developer has it set to initialize as a blank "" for its contents and corresponding Name.
At runtime they update this text field with a few sentences, and I am unable to see this update in inspect.exe or in my own AutomationElement object. Is there a way to "getText" other than AutomationElement.Current.Name or variants thereof?
The only Pattern Available on the AutomaitonElement is "IsLegacyIAccessiblePatternAvailable", and even this pattern maintains a non-changing blank Name and Help attribute.
Is this not possible within the scope of UIAutomation to demand the current string being displayed in an object? Should I request the developer add another control pattern that will make this accessible? If so which one?
Ideally I think automation should have a minimal impact on the development and design process, with exceptions for things like AutomationID being defined- is this philosophy itself flawed?
I'm very new to Selenium RC. I'm using .NET (though I don't think it is relevant), I have opened a page, but I want to confirm that the page was actually opened. I have a few ideas like using .Select() or using one of the .get*() methods, but I want to do what is considered the best practice by others in the Selenium community.
I usually assert some text or element on the page. You most likely don't just want to make sure the page "loaded" you want to make sure it loaded something specific
I am currently confirming the page was returned using the following:
[Then(#"the (.*) page should be displayed")]
public void ThenThePageShouldBeDisplayed(string pageName) {
Assert.IsTrue(selenium.GetLocation().Contains(pageName));
}
This happens to be a SpecFlow test step implementation.