We are building a web application that has expandable workspaces on the sides of the page that expand and contract based on page size. when the workplace is contracted the page objects of the expanded list are not visible to be initialized when the class is called as the site is being developed in Angular 2.0
We are also building an automated test framework with Selenium with C# bindings and using the Page Object Model to run our automation. Is it possible load objects based on the window into a single page class like this
public class PageObjectClass
{
public PageObjectClass(IwebDriver driver)
{
PageFactory.InitElements(driver, this)
}
private IWebElement anObjectVisibleWhenContracted
//load an object that is not visible based on window size
}
Or do I need to get the size of the window when I call the class and have separate classes based on window width in the test scripts like this?
if (driver.Manage().Window.Size.Width < 1280)
{
someVar = new PageObjectClass(driver):
}
else
{
someOtherVar = new exp[andedPageObjects
}
//do stuff here
If you don't want to face issues when an element is displayed but Selenium doesn't interact with it a good practice is to maximize your browser's window on setUp.
Still if you don't want to do this, Selenium scrolls to an element when you interact with it.
So, answering your question - you don't need to change the window size. When you use PageObject in C# like you shown then each element will be initialized when you address to it (click, sendKeys, etc.). Each time you address to an element it will be initialized again and Selenium must scroll to this element.
But there are some bugs that appears in some cases when an element is displayed at the edge of the page and Selenium can not scroll to it correctly. Why this happens I don't know but luckily it happens very rarely.
I figured this out on my own. In the classes where I needed to deal with expandable workspaces, I declared all my variables in the class and didn't assign them any values before running PageFactory.InitElements
In the class constructor I passed in a Size variable along with the WebDriver that has the current size of the window. Then the objects that were appropriate for the window size were all loaded based on that
Just took a little restructuring and now it's working like a charm. Class structure looks like this now
public class ClassName: InheritedClass
{
#region Page Objects
private IWebElement object1;
private IWebElement object2;
#endregion
public ClassName(IWebDriver driver, Size winSize)
{
PageFactory.InitElements(driver, this);
if (winSize.Width > 1440)
{
object1= driver.FindElement(expanded By phrase locator);
object1 = driver.FindElement(expanded By phrase locator);
}
else
{
object1= driver.FindElement(contracted By phrase locator);
object1 = driver.FindElement(contracted By phrase locator);
}
}
#region Page Methods that use these objects
#endregion
}
Related
Hopefully I'm not the first person to encounter this issue.
I'm writing some selenium tests in C# and have a dilemma when trying to adobt a page object model design whilst also needing to do some explicit waits with the ExpectedConditions class.
Let's say I'm storing my elements in an element map class that is simply a property that calls the .FindElement method using an XPath stored in a resources file...
public class PageObject {
public IWebElement Element
{
get { return DriverContext.Driver.FindElement(By.XPath(Resources.Element)); }
}
}
Then I would go on to use that property in various selenium methods.
The issue I have is I also need to check whether this element is visible on the page, and it will error before I can perform the checked (e.g. with WebDriverWait, passing in ExpectedConditions.ElementIsVisible(by) to the .until method).
How do I cleanly seperate out the IWebElement and By locator and allow for this explicit wait/check where needed?
TLDR - How do I maintain a Page Object Model design whilst also having the flexibility to use explicit waits based on the By locator of my elements.
Many thanks,
I use page objects all the time but I have locators at the top of the class instead of elements. I then use the locators to click buttons, etc. as needed. The advantage of this is I only access the element on the page when needed which avoids stale element exceptions, etc. See a simple example below.
class SamplePage
{
public IWebDriver Driver;
private By waitForLocator = By.Id("sampleId");
// please put the variable declarations in alphabetical order
private By sampleElementLocator = By.Id("sampleId");
public SamplePage(IWebDriver webDriver)
{
this.Driver = webDriver;
// wait for page to finish loading
new WebDriverWait(Driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.PresenceOfAllElementsLocatedBy(waitForLocator));
// see if we're on the right page
if (!Driver.Url.Contains("samplePage.jsp"))
{
throw new InvalidOperationException("This is not the Sample page. Current URL: " + Driver.Url);
}
}
public void ClickSampleElement()
{
Driver.FindElement(sampleElementLocator).Click();
}
}
I would recommend against storing locators in a separate file because it breaks one of the mantras of page object model which is everything to do with the page goes in the page object. You shouldn't have to open anything but one file to do anything with Page X, the page object class.
I'm attempting to use CodedUI in a code-first approach (page object pattern) for a WPF UI. I'm able to navigate to a specific list item within a groupbox within a tab on the main window. Each list item contains a checkbox along with some other content; I'd like to automate clicking the checkbox, but I'm getting an exception with the message 'Search may have failed at " TabList as it may have virtualized children...'
The only thing is that I'm setting the containing WpfListItem as the parent for the WpfCheckBox per the following code:
public class ConfigItem
{
private readonly WpfListItem _instance;
public WecoConfigItem([NotNull] WpfListItem instance)
{
if (instance == null) throw new ArgumentNullException("instance");
_instance = instance;
}
public ConfigItem SelectConfiguration()
{
var checkBox = new WpfCheckBox(_instance);
_instance.DrawHighlight();
checkBox.SearchProperties.Add(WpfCheckBox.PropertyNames.AutomationId, "cbIsSelected");
Mouse.Click(checkBox);
return this;
}
}
The failure occurs in the SelectConfiguration method. During test execution, the corresponding ListItem is highlighted, but then in the html output the recorded image highlights the application. So, some questions:
Why is the search starting from the application window when I'm providing the WpfListItem as the parent in the constructor?
Am I doing something that is causing the discrepancy between the DrawHighlight() output and the HTML output?
How do I constrain the search to begin with the WpfListItem parent object, for a code-first page object pattern approach?
EDIT: The search is actually beginning from the top-level application, not the tab - I was looking at a stale HTML log. Problem statement is still essentially the same.
The moment you call DrawHighlight() you are initiating a search. The next statement then gives additional search criteria and then you access the control again (Mouse.click()), but then you are reading from cache. I assume that you either need to disable the cache by setting the SearchOptions to AlwaysSearch or add the criteria before you call DrawHighlight().
Sometimes when i wrote i had to eg edit a text in TextBlock which is on other Page, then I did this trick:
private static MainPage _gui;
public static void SetTitleText(string text) => _gui.TitleText.Text = text;
public static Visibility InfoCenterStackPanelVisibility
{
get { return _gui.InfoCenterStackPanel.Visibility; }
set { _gui.InfoCenterStackPanel.Visibility = value; }
}
public MainPage()
{
InitializeComponent();
_gui = this;
}
And in other page i just call MainPage.SetTitleText etc;
And I want to know, is any other, better way to acces these items, without creating _gui?
ps I looked for any likely looking topics, I did not found an answer
Generally this is a bad idea because if your app gets complex enough you will end up in a situation where your MainPage doesn't exist at runtime, and thus will crash when you try and access the (null) static instance. This could happen eg if you implement Suspend / Terminate / Resume in a multi-page app, or you implement a contract like Share. Even if your current project is simple, this is a bad habit to get into if you build larger apps in the future.
Typically you would use an MVVM approach to have this information in your data model, and then the MainPage would just bind to the data model (or explicitly read values out of it). There are a lot of web resources for MVVM - just use your favourite search engine.
Does anyone know of a good component (C# WinForms) which would allow creating an options (settings) form, given a custom class with a bunch of properties? I am not looking for something shiny, but something merely better than a property grid. I can easily take care of the visual part, but I simply don't want to lose time doing reflection to add and bind controls if it already exists.
I am pretty sure I've seen a Visual Studio options-like form somewhere before, which was created dynamically (with some attributes attached to the properties of the class, to allow grouping and additional info).
[Edit] For example, I might have an options class:
public class Options : SerializableOptions<Options>
{
[Category("General")]
[Name("User name")]
[Description("Some text")]
public string Username { get; set; }
[Category("General")]
[Name("Log in automatically")]
public bool LogInAutomatically { get; set; }
[Category("Advanced")]
// ConnectionType is enum
public ConnectionType ConnectionType { get; set; }
// ...
}
After passing it to this form, it would create two panels ("General" and "Advanced"), with a CheckBox and a TextBox on the first panel, and one ComboBox (with all available enums) on the second panel.
If there isn't such a control, what do you guys use? Manually add, populate, format and bind controls for each option?
I'm not aware of any controls that allow you to do this, but it isn't difficult to do yourself. The easiest way is to create the dialog shell, a user control which acts as the base class for the options "panels", one (or more) attribute to control the name and grouping information, and an interface (which the user control implements).
Each of your custom options panels derives from the user control and overrides some sort of Initialize() and Save() method (provided by the user control). It also provides your attribute (or attributes) that determine the name/grouping information.
In the dialog shell, reflectively inspect all public types from your assembly (or all loaded assemblies) looking for types that implement your interface. As you find a type, get the attributes to determine where to place it in your grouping (easiest thing here is to use a tree view), call Activator.CreateInstance to create an instance of the user control and store it in the Tag property. When the user clicks on an entry in the grouping (a tree node), get the Tag and set the panel which contains the user control to the object in the Tag property. Finally, when the user clicks "OK" on the dialog, loop through the tree nodes, get the Tag property and call the Save method.
Update:
Another option would be to use a property grid control. It doesn't have a "pretty" UI look to it, but it is very functional, already supports grouping by a category attribute, and allows a great deal of flexibility. You could go with a single property grid that shows all of the options, or go with a "hybrid" approach with a tree view that groups by major functions (plugin, capability, etc.), probably based on the type. When the user clicks that node, give the property grid the object instance. The only drawback to this approach is that when changes are made to the property grid values they are "live" in that the underlying property is immediately changed, which means there is no concept of "Cancel" short of saving a copy of each value that could change and performing some type of "reset" yourself.
I don't know if such a control exists, but writing the required reflection code is really not that hard. E.g. something like this:
// the class for which to create an UI
public class MyClass
{
public string Text { get; set; }
public int ID { get; set; }
}
...
// basic reflection code to build the UI for an object
var obj = new MyClass() { Text="some text", ID=3};
foreach (var pi in obj.GetType().GetProperties())
{
var name = pi.Name;
var type = pi.PropertyType;
var value = pi.GetValue(obj, null);
//now setup the UI control for this property and display the value
}
I accidentally found something similar to this, I remebered that I had this problem a while ago and thought I should share it.
Here is a simple example: http://blog.denouter.net/2008/08/simple-reflection-form.html. It uses reflection to create several controls based on object's properties.
I have subclassed Form to include some extra functionality, which boils down to a List<Image> which displays in a set of predefined spots on the form. I have the following:
public class ButtonForm : Form
{
public class TitleButton
{
public TitleButton() { /* does stuff here */ }
// there's other stuff too, just thought I should point out there's
// a default constructor.
}
private List<TitleButton> _buttons = new List<TitleButton>();
public List<TitleButton> TitleButtons
{
get { return _buttons; }
set { _buttons = value; }
}
// Other stuff here
}
Then my actual form that I want to use is a subclass of ButtonForm instead of Form. This all works great, Designer even picks up the new property and shows it up on the property list. I thought this would be great! It showed the collection, I could add the buttons into there and away I would go. So I opened the collection editor, added in all the objects, and lo and behold, there sitting in the designer was a picture perfect view of what I wanted.
This is where it starts to get ugly. For some reason or another, Designer refuses to actually generate code to create the objects and attach them to the collection, so while it looks great in Design mode, as soon as I compile and run it, it all disappears again and I'm back to square one. I'm at a total loss as to why this would happen; if the Designer can generate it well enough to get a picture perfect view of my form with the extra behaviour, why can't/won't it generate the code into the actual code file?
First of all you need to inherit your TitleButton class from Component so that the designer knows it is a component that can be created via designer generated code. Then you need to instruct the designer code generator to work on the contents of the collection and not the collection instance itself. So try the following...
public class TitleButton : Component
{
// ...
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<TitleButton> TitleButtons
{
// ...
}