When I declare my IWebElement like this:
[FindsBy(How = How.CssSelector, Using = "input#raffle_submit")]
private IWebElement _buyNowButton;
And initialize it in the class constructor using PageFactory:
PageFactory.InitElements(Driver, this);
Then when I make a break point in any method in order to look at the element's properties I see no properties in this element:
public CartObj ClickBuyNowButton()
{
_buyNowButton.Click(); //here is my break point
}
Only property I see is: - Non-Public members : http://prntscr.com/8k90r4
So where are all properties that must be in IWebElement like "Enabled", "Displayed" and so forth?
More to say: I don't have any problems with using this element, I can click on it, I can send keys to it but when I use the next JavaScript code it tells me that argument is wrong (but it worked well before):
((IJavaScriptExecutor)Driver).ExecuteScript("arguments[0].scrollIntoView();", webElement);
Error that I appears after executing JS code above:
Additional information: Argument is of an illegal
typeOpenQA.Selenium.Support.Events.EventFiringWebDriver+EventFiringWebElement
I'm sure it's connected with version of WebDriver.
So my current WebDriver version is 2.47.0
ChromDriver version is 2.19
To your first question: What you see in the debugger is actually just a proxy object. The PageFactory initializes your _buyNowButton with a proxy which will only be resolved once you use it somewhere in your script. So unless you actually use it, you won't see any properties like Displayed or Enabled in the debugger.
As to your second question: I can't recreate that now with Selenium 3 and the latest ChromeDriver, but it may have been a bug back in the day.
Related
I work with Specflow (if there is such a function there)
Programming language C#.
I want a screenshot to be taken after each selenium action.
Why? -> I want that after the first automation, a series of pictures of the successful test run is created (so that business analysts in the future can quickly see via pictures, what is tested with which test caseid).
It should be possible to specify via a flag whether the screenshot should be created or not. So my main problem is:
How do I ensure that my screenshot function is automatically called after each "driver.FindElement.... "and then "variable.click" or similar?
I don't want to manually insert "makeScreenshot()" everywhere.
It would be wonderful if a different method could be called after each method call. That looks up what the "command is". If the syntax then matches something like "Click()", the screenshot is triggered, otherwise not.
If there is no such thing. Something like "Screenshot(variable.Click())" would also be conceivable. That would still be clean and easily implemented everywhere via copy paste.
Does anyone have an idea or a link? (I'm a bit of a noob and need code examples to get started)
Potential solution 1:
You could add a extension method to your project for IWebElement, which would allow you to create your own custom click and find methods that screenshot after clicking. For example:
public static class IWebElementExtensions
{
public static void ClickAndScreenshot(this IWebElement element,
IWebDriver driver,
ScenarioContext scenarioContext)
{
element.Click();
Screenshot ss = ((ITakesScreenshot)driver).GetScreenshot();
ss.SaveAsFile($"{scenarioContext.ScenarioInfo.Title}_{scenarioContext.StepContext.StepInfo.Text.Replace("\"", "")}.png",
ScreenshotImageFormat.Png);
}
}
If you add the above class to your project and reference it in your steps, it would click the element, and then take a screenshot and add the scenario and step info to the filename to identify it later (consider how you plan to manage these screenshots, might be worth adding a subfolder per scenario, or datetimes to the filename for diagnostics).
You can then make use of this method in your Specflow steps:
[When(#"I click a login button")]
public void WhenIClickALoginButton()
{
By someLoginButton = By.Id("login");
_driver.FindElement(someLoginButton).ClickAndScreenshot(_driver, _scenarioContext);
}
You could do something similar for finding the element. You could create an extension method that finds the element, takes a screenshot and then returns the element as a return type to be used by your WebDriver.
You would have to change your current click() methods to take advantage of this new method (find and replace should help make this trivial), but your screenshot code would all be in one place if you need to change it. Doing it this way means you can pick and choose when to call that method as well depending on your steps, as you could stick to the original Click() method if not interested in a screenshot in a particular step.
Potential Solution 2:
I feel like taking a screenshot after every Specflow step rather then each Selenium action is worth mentioning as a potential solution, focusing on chunks of user actions rather then every Selenium action.
For example, you could configure a hooks file to take a screenshot after every specflow step:
[Binding]
public class Hooks
{
IWebDriver _driver;
public Hooks(IWebDriver driver)
{
_driver = driver;
}
[Scope(Tag = "screenshot")]
[AfterStep]
public void TakeScreenshotAfterStep(ScenarioContext scenarioContext)
{
Screenshot screenshot = ((ITakesScreenshot)_driver).GetScreenshot();
screenshot.SaveAsFile($"{scenarioContext.ScenarioInfo.Title}_{scenarioContext.StepContext.StepInfo.Text}.png",
ScreenshotImageFormat.Png);
}
[AfterScenario]
public void AfterScenario()
{
_driver.Dispose();
}
}
For a example Feature:
Feature: Login
#screenshot
Scenario: Successful Login
GIVEN I navigate to stack overflow
WHEN I login
THEN I am logged in
#someOtherTag
Scenario: API test with no UI
GIVEN I have a cool API
WHEN I do a GET
THEN I GET some cool stuff
This would generate 3 screenshots after each step for the Successful login scenario, but it wouldn't take any for the API test due to the tag scope on the after step, which should tick the box around being able to specify when to do this.
The above relies on your Specflow steps not being too large though... if you're doing multiple large chunks of user actions within one step, it may skip a lot, but if that's only a rare occurrence you could do some extra screenshot taking within those steps using the extension from potential solution 1.
I hope this helps, I apprieciate it's not a pure answer but from experience of when these sorts of screenshots can be useful, I'd recommend solution 2 and use it as an opportunity to make sure your Specflow steps aren't doing too much.
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 have basic understanding of Selenium architecture, where Search Context is main interface implemented by Web Driver interface which is extended by various browser driver class. Normally, we follow POM for our selenium projects and define each object by their By class -
By addButton=By.Id("asdf");
But just realized that we can even do-
addButton.FindElement(By.XPath("ABC").
But this does not return element like driver.FindElement(addButton)
When to use above statement?
The following is using terms from the Java-binding, but it holds true for the C#-Binding as well:
First
#spcial is right there is no "By.findElement(By)" defined in Selenium. Nevertheless there is a "By.findElement(SearchContext)" defined which I will explain below:
SearchContext vs. By
In Selenium you have an interface called a SearchContext and then you have the By class.
A SearchContext can be a WebElement or a WebDriver
Now you have two options to find an element (using pseudo-code):
1) SearchContext.findElement(By...)
or
2) By.findElement(SearchContext...)
Both do the same thing!
Say in your case you have a driver and the By variable like this:
WebDriver driver = new FirefoxDriver();
By addButtonLocator = By.id("asdf");
now you can find your element in two ways:
1) driver.findElement(addButtonLocator);
or
2) addButtonLocator.findElement(driver);
Again! Both do the same thing, it's just another way to "read" these expressions like this:
1) "take the driver and search for an element using this By-statement"
or
2) "take the By-statement and search for an element that fits this statement within driver"
As said before, instead of a driver you can have a smaller scope if you use an already identified element.
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."
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.