I'm struggling with finding by Xpath. My problem is that the software that I'm testing has one specific function where I need to scan two components (is it called two-step scan) and there are two textboxes without the name and with the same Automationid. So I need to find the second one I tried this but it does not work.
[FindsBy(How = How.Xpath, Using = "//*[#AutomationId='ScanTextBox'][1]")]
public IWebElement ScanTextBox1;
[FindsBy(How = How.Xpath, Using = "//*[#AutomationId='ScanTextBox'][2]")]
public IWebElement ScanTextBox2;
I'm using winium and I'm testing WPF application.
Update locators as described here:
(//*[#AutomationId='ScanTextBox'])[1]
(//*[#AutomationId='ScanTextBox'])[2]
The difference is that in my case (locator)[n]you select n-th element out of all elements found by a locator. And bylocator[n] you search for element that has n-th positions inside parent nodes
You really need to use a FindsBy? We are unable to create a correct XPath to a FindsBy to these buttons without the html. This way, I only can provide you another solution, and update my awnser when the html code be provided.
You can use FindElement, the plural one, that is able to return both. After, you select the desired button by index. A example method able to do it:
public IWebElement GetScanTextBox(int index)
{
return Driver
.FindElements(By.XPath("//*[#AutomationId='ScanTextBox']"))
.ElementAt(index);
}
public void UsageExample()
{
var buttonOne = GetScanTextBox(0);
var buttonTwo = GetScanTextBox(1);
}
Related
I have IWebElements defined like this:
[FindsBy(How = How.Id, Using = "actionHistoryBtn")]
private IWebElement actionHistoryButtons;
But the only way I have been able to find elements with the same Id is by doing this:
private ReadOnlyCollection<IWebElement> actionHistoryButtons => driver.FindElements(By.Id("actionHistoryBtn"));
Is there a way I can create a collection in the same format? Or how I can modify this to get all elements using "WhateverId" Or is the ReadOnlyCollection the only way to get all the elements?
To simply answer your question, ReadOnlyCollection<IWebElement>, a collection of elements, is what driver.FindElements() returns so that's the data type you need to use. You can't use IWebElement because that represents a single element, not a collection of elements like you are looking for.
Having said that... some advice.
Simon Stewart, the Selenium Project lead and creator of PageFactory, has said not to use PageFactory. Here's a keynote from a seleniumconf a few years ago where he states this.
Keynote - Selenium: State of the Union - Simon Stewart – Selenium Project & WebDriver
25:18 Don't use PageFactory
Besides the fact that Simon said not to use it, it's best not to have state in your page objects, e.g. don't grab elements and store them in properties. Instead grab them as you need them... that will reduce StaleElementExceptions and other issues.
Here's a real quick sample page object showing what I'm describing.
class SamplePage
{
private readonly By _actionHistoryButtonsLocator = By.Id("actionHistoryBtn");
public ReadOnlyCollection<IWebElement> GetActionHistoryButtons()
{
return Driver.FindElements(_actionHistoryButtonsLocator);
}
}
What are best practices for accessing selectors, when you use page object pattern? Let's have a simple example:
[FindsBy(How = How.CssSelector, Using = "button-submit")]
public IWebElement button { get; set; }
in most cases you use just button.Click() etc., and that's simple, but what if sometimes you need to access the css selector, as you traverse in shadow DOM for example, so you need to write something like GetShadowElement(anotherButton).FindElement(By.CssSelector("button-submit"));.
Is there a way to access this Using= from page element while working with FindElement method or do i need to extract this into:
private const string locator = "button-submit"; for example, update my page object element into Using = locator and of course later on collect all such examples and put them in static class for constants, etc. Is there a better way for handling that? How do you traverse in shadow DOM with page object?
I am building a Page Object Model in Selenium WebDriver for C#, using the PageFactory.
Unfortunately, I have discovered that the FindsByAttribute will not initialize a SelectElement (HTML <select> tag / dropdown menu). I've happened upon or come up with a few ideas to work around it so far, but none of them is ideal:
PageFactory and FindsByAttribute are sealed, so I can't force it to by just inheriting those.
Manually instantiating a SelectElement from an IWebElement in each method is rather messy and duplicative. It also ignores the apparent built-in wait in PageFactory and throws NoSuchElementExceptions unless I add a wait every time I do this -- which would require repeating the locator all over the place, defeating (part of) the purpose of the POM.
Wrapping each IWebElement property with a SelectElement property is less messy, but still has the same waiting problem as above.
The best option so far is #3, and writing a wrapper for SelectElement that just adds a wait to every method. While this solution will work, it will bulk up the code of each page a lot, as instead of this (hypothetical) pretty code:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
public SelectElement MonthDropdown;
I'm stuck with a wrapper wrapper (something I'd rather avoid), and:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement _monthDropdown;
public Selector MonthDropdown
{
get { return new Selector(MonthDropdown, Wait); }
}
With Selector being the SelectElement wrapper, that also has to take in the IWait<IWebDriver> so it can wait, and instantiating a new Selector every time I access it.
Is there a better way of doing this?
EDIT: Sleepily put in wrong access modifiers. Fixed. Thanks, #JimEvans.
First, there's no "built-in wait" in the .NET PageFactory implementation. You can easily specify one in the call to InitElements (more on that in a bit). At present, the best option for you would be your option 3, though I wouldn't expose the IWebElement member; I'd make it private, since the PageFactory can enumerate over private members just as easily as public ones. So your page object would look like this:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement dropDown;
public SelectElement MonthDropdownElement
{
get { return new SelectElement(dropdown); }
}
How do you get the actual IWebElement when you need it? Since SelectElement implements IWrappedElement, you can simply call the WrappedElement property if you need access to the methods and properties of the element provided by the IWebElement interface.
Recent versions of the .NET bindings have restructured the PageFactory to be more extensible. To add the "built-in wait" you desire, you could do the following:
// Assumes you have a page object of type MyPage.
// Note the default timeout for RetryingElementLocator is
// 5 seconds, if unspecified.
// The generic version of this code looks like this:
// MyPage page = PageFactory.InitElements<MyPage>(new RetryingElementLocator(driver), TimeSpan.FromSeconds(10));
MyPage page = new MyPage();
PageFactory.InitElements(page, new RetryingElementLocator(driver, TimeSpan.FromSeconds(10)));
Additionally, if you really need to customize how things work, you're always welcome to implement IPageObjectMemberDecorator, which allows you to fully customize how attributes are enumerated and values set to the properties or fields decorated with those attributes. One of the (non-generic) overloads of PageFactory.InitElements takes an instance of an object implementing IPageObjectMemberDecorator.
I'll leave aside that proper implementations of the Page Object Pattern as strictly defined shouldn't expose any WebDriver objects outside of each page object. Otherwise, all you're implementing is a "page wrapper," which is a perfectly valid approach, just not what one would call a "page object."
What is the standard approach when applying the PageObjects pattern to components of a page?
For the sake of an example lets say I am writing tests for the Features on an Amazon product page.
That page contains a large number of separate features, Product Information, Customers who Viewed this, Other Customers Suggested etc etc.
Current examples I have seen for PageObjects really only cover how to deal with a single page that has limited functionality. What I am looking for is something along the line of a PageObject that would represent the Product page and then be composed of ComponentObjects that represent each component.
eg:
public class ProductPage
{
[FindsBy(How = How.Id, Using = "productInformation")]
public ProductInformationControl ProductionInformation{get;set}
[FindsBy(How = How.Id, Using = "customersViewed")]
public CustomersAlsoViewedControl CustomersAlsoViewed{get;set}
[FindsBy(How = How.Id, Using = "customersSuggested")]
public CustomersSuggestedControl CustomersSuggested{get;set}
}
public class ProductInformationControl
{
//Ideally the FindsBy here would find the element based on the Controls context
// So only resolving to an element that was within the context of this control.
[FindsBy(How = How.ClassName, Using = "title")]
private IWebElement _title;
public string Title
{
get
{
return _title.Text;
}
}
}
Then within a test I would access the control like so:
var productPage = ScenarioContext.Current.Get<ProductPage>();
Assert.That(productPage.ProductInformation.GetTitle(), Is.EqualTo("My Title"));
I've used this approach previously but with a custom framework built on top of Selenium that allowed for resolution of the child objects and their components. What I'm looking for is the approach to take with Selenium 2 and C# out of the box.
There is no out of the box way to do this.
Your approach is very good,
you can implement it by calling PageFactory.InitElements() with custom implementation for IElementLocator and IPageObjectMemberDecorator
See this answer:
How to initialize SelectElements while using PageFactory / FindsBy in Selenium C#?
I am very new to Selenium, so my apologies if it's a silly question.
I have successfully wired up IntelliJ (Play! framework) with Selenium, and created some tests using firefoxDrivers.
I'm trying to check if the page had been validated properly.
long story short, I'm selecting an element like this:
WebElement smallDecel = firefoxDriver.findElement(By.cssSelector("#configTable tr:nth-child(2) td .playerDecelInput"));
I do some further operations (clear and change the value, submit the 'form'), and then I want to check if the TD the input sits in was given another class.
So, the question is - is there a simple technique I can use to find out if a WebElement / DOM has a class specified?
To expand on Sam Woods' answer, I use a simple extension method (this is for C#) to test whether or not an element has a specified class:
public static bool HasClass( this IWebElement el, string className ) {
return el.GetAttribute( "class" ).Split( ' ' ).Contains( className );
}
Once you find the element, you can just call myElement.GetAttribute("class"). Then you can parse the string that is returned and see if it contains or does not contain the class name you care about.
You can use FindElement(By.ClassName(//name of your class)); I would recommend that you either loop through and search the DOM for a set period of time or set a Thread.sleep(xxxx) and then look for the appended class.