Fix Element not Interactable Selenium C# - c#

I am trying to create a raffle autocheckout on feature.com using C# and Selenium. I got through everything up to the payment page, where the payment fields (card,expiry, etc) are "not interactable". I've tried everything from using tag names, xpath, and nothing. This is my code:
const string cardName = "John A Doe";
const string expiry = "1224";
const string cvv = "001";
driver.FindElement(By.XPath("/html/body/div[3]/main/div[2]/div/div[3]/div[2]/div/div/div[2]/div/form/div[1]/div/input")).SendKeys(card);
driver.FindElement(By.Id("card-holder")).SendKeys(cardName);
driver.FindElement(By.Id("card-expiry-date")).SendKeys(expiry);
driver.FindElement(By.Id("card-cvc")).SendKeys(cvv);

My guess is that since this is a payment, it is probably in an iframe. You will need to switch to it and then add the cc info and the switch to main window to continue.
That first xpath needs to be fixed. That is a recipe for failure. If you post the html we can help you with a better path.
I just realized you posted the site. It is in an iframe and you can't get to the card name because it is in an iframe.
Based on this I would used the below but these fields are hidden which is always fun to deal with.
SwitchToIframeByXpath("//iframe[contains(#id, 'card-fields-number')]");
driver.FindElement(By.Xpath("//input[#placeholder='Card number']")).SendKeys(cardNum);
SwitchToMainContent;
SwitchToIframeByXpath("//iframe[contains(#id, 'card-fields-name')]");
driver.FindElement(By.Xpath("//input[#placeholder='Name on card']")).SendKeys(cardName)
SwitchToMainContent;
Rinse repeat for each field.
public static void SwitchToIframe(string frameName)
{
driver.SwitchTo().Frame(frameName);
}
or by xpath
public static void SwitchToIframeByXpath(string title)
{
driver.SwitchTo().Frame(driver.FindElement(By.XPath(title)));
}
Then switch to main
public static void SwitchToMainContent()
{
driver.SwitchTo().DefaultContent();
}

Related

Selenium: Get the string out of a email textfield/textbox

I want to get an email string out of an email textbox which was recently entered.
I'm doing automation tests with selenium and to confirm that the correct string was entered into the textbox, I want to recheck it before it goes on to the password textbox.
I saw a lot of examples here but the most are either getText(); (which seems to not work anymore) or getAttribute("Value");.
I debugged it, and the checkText gives always Null.
What im currently having is this code:
public static void SendKeysElement(IWebDriver webDriver)
{
IWebElement Field = webDriver.FindElement(By.XPath(selector));
Field.SendKeys("example#example.com");
string checkText = Field.GetAttribute("Value");
if (checkText != "example#example.com")
{
Console.WriteLine("String is wrong");
}
else
{
ConsoleWriteLine("String is correct");
}
}
Here is the inspect of that textbox, while the email string was entered.
The first thing I notice is, that the entered string in the email textbox is not displayed in the inspect.
The webpage is being written with .NET using a blazor template.
Instead of
.GetAttribute("Value")
try this :
.GetAttribute("value");
Note that, attribute type is case sensitive.

How to prevent "stale element" inside a foreach loop?

I'm using Selenium for retrieve data from this site, and I encountered a little problem when I try to click an element within a foreach.
What I'm trying to do
I'm trying to get the table associated to a specific category of odds, in the link above we have different categories:
As you can see from the image, I clicked on Asian handicap -1.75 and the site has generated a table through javascript, so inside my code I'm trying to get that table finding the corresponding element and clicking it.
Code
Actually I have two methods, the first called GetAsianHandicap which iterate over all categories of odds:
public List<T> GetAsianHandicap(Uri fixtureLink)
{
//Contains all the categories displayed on the page
string[] categories = new string[] { "-1.75", "-1.5", "-1.25", "-1", "-0.75", "-0.5", "-0.25", "0", "+0.25", "+0.5", "+0.75", "+1", "+1.25", "+1.5", "+1.75" };
foreach(string cat in categories)
{
//Get the html of the table for the current category
string html = GetSelector("Asian handicap " + asian);
if(html == string.Empty)
continue;
//other code
}
}
and then the method GetSelector which click on the searched element, this is the design:
public string GetSelector(string selector)
{
//Get the available table container (the category).
var containers = driver.FindElements(By.XPath("//div[#class='table-container']"));
//Store the html to return.
string html = string.Empty;
foreach (IWebElement container in containers)
{
//Container not available for click.
if (container.GetAttribute("style") == "display: none;")
continue;
//Get container header (contains the description).
IWebElement header = container.FindElement(By.XPath(".//div[starts-with(#class, 'table-header')]"));
//Store the table description.
string description = header.FindElement(By.TagName("a")).Text;
//The container contains the searched category
if (description.Trim() == selector)
{
//Get the available links.
var listItems = driver.FindElement(By.Id("odds-data-table")).FindElements(By.TagName("a"));
//Get the element to click.
IWebElement element = listItems.Where(li => li.Text == selector).FirstOrDefault();
//The element exist
if (element != null)
{
//Click on the container for load the table.
element.Click();
//Wait few seconds on ChromeDriver for table loading.
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(20);
//Get the new html of the page
html = driver.PageSource;
}
return html;
}
return string.Empty;
}
Problem and exception details
When the foreach reach this line:
var listItems = driver.FindElement(By.Id("odds-data-table")).FindElements(By.TagName("a"));
I get this exception:
'OpenQA.Selenium.StaleElementReferenceException' in WebDriver.dll
stale element reference: element is not attached to the page document
Searching for the error means that the html page source was changed, but in this case I store the element to click in a variable and the html itself in another variable, so I can't get rid to patch this issue.
Someone could help me?
Thanks in advance.
I looked at your code and I think you're making it more complicated than it needs to be. I'm assuming you want to scrape the table that is exposed when you click one of the handicap links. Here's some simple code to do this. It dumps the text of the elements which ends up unformatted but you can use this as a starting point and add functionality if you want. I didn't run into any StaleElementExceptions when running this code and I never saw the page refresh so I'm not sure what other people were seeing.
string url = "http://www.oddsportal.com/soccer/europe/champions-league/paok-spartak-moscow-pIXFEt8o/#ah;2";
driver.Url = url;
// get all the (visible) handicap links and click them to open the page and display the table with odds
IReadOnlyCollection<IWebElement> links = driver.FindElements(By.XPath("//a[contains(.,'Asian handicap')]")).Where(e => e.Displayed).ToList();
foreach (var link in links)
{
link.Click();
}
// print all the odds tables
foreach (var item in driver.FindElements(By.XPath("//div[#class='table-container']")))
{
Console.WriteLine(item.Text);
Console.WriteLine("====================================");
}
I would suggest that you spend some more time learning locators. Locators are very powerful and can save you having to stack nested loops looking for one thing... and then children of that thing... and then children of that thing... and so on. The right locator can find all that in one scrape of the page which saves a lot of code and time.
As you mentioned in related Post, this issue is because site executes an auto refresh.
Solution 1:
I would suggest if there is an explicit way to do refresh, perform that refresh on a periodic basis, or (if you are sure, when you need to do refresh).
Solution 2:
Create a Extension method for FindElement and FindElements, so that it try to get element for a given timeout.
public static void FindElement(this IWebDriver driver, By by, int timeout)
{
if(timeout >0)
{
return new WebDriverWait(driver, TimeSpan.FromSeconds(timeout)).Until(ExpectedConditions.ElementToBeClickable(by));
}
return driver.FindElement(by);
}
public static IReadOnlyCollection<IWebElement> FindElements(this IWebDriver driver, By by, int timeout)
{
if(timeout >0)
{
return new WebDriverWait(driver, TimeSpan.FromSeconds(timeout)).Until(ExpectedConditions.PresenceOfAllElementsLocatedBy(by));
}
return driver.FindElements(by);
}
so your code will use these like this:
var listItems = driver.FindElement(By.Id("odds-data-table"), 30).FindElements(By.TagName("a"),30);
Solution 3:
Handle StaleElementException using an Extension Method:
public static void FindElement(this IWebDriver driver, By by, int maxAttempt)
{
for(int attempt =0; attempt <maxAttempt; attempt++)
{
try
{
driver.FindElement(by);
break;
}
catch(StaleElementException)
{
}
}
}
public static IReadOnlyCollection<IWebElement> FindElements(this IWebDriver driver, By by, int maxAttempt)
{
for(int attempt =0; attempt <maxAttempt; attempt++)
{
try
{
driver.FindElements(by);
break;
}
catch(StaleElementException)
{
}
}
}
Your code will use these like this:
var listItems = driver.FindElement(By.Id("odds-data-table"), 2).FindElements(By.TagName("a"),2);
Use this:
string description = header.FindElement(By.XPath("strong/a")).Text;
instead of your:
string description = header.FindElement(By.TagName("a")).Text;

test all website links with selenium webdriver c#

I want to test if there are no broken links on the website by getting all the links in list, clicking them and getting response if they are working. Can you suggest me a way to do it in c#?
namespace billingtest
{
[TestClass]
public class test
{
FirefoxDriver driver;
[TestInitialize()]
public void SyncDriver()
{
driver = new FirefoxDriver();
driver.Manage().Window.Maximize();
}
[TestMethod]
public void testwithadmin()
{
driver.Navigate().GoToUrl("http://localhost:52982");
driver.FindElement(By.Id("UserNameOrEmail")).SendKeys("aa");
driver.FindElement(By.Id("Password")).SendKeys("aa");
driver.FindElement(By.XPath(".//*[#id='main']/form/div[3]/input")).Click();
driver.FindElement(By.XPath(".//*[#id='content-main']/div/div/a[3]")).Click();
//Get all links, click them one by one and get response if they are working
}
[TestCleanup]
public void TearDown()
{
driver.Quit();
}
}
}
Check it using getTitle();
if it return something then go ahead
and if return false (means blank) then print that link.
There are a few things you could do depending on what you are looking for as a PASS. You could start by getting all the links on the page and iterating through the collection. That code is below. If you want to stay in the browser, you could click on each link and open it in a new browser window and verify the page. If you don't care about staying in the browser, you could get the href from each link and use this answer to validate the URLs.
ReadOnlyCollection<IWebElement> links = driver.FindElements(By.TagName("a"));
foreach (IWebElement link in links)
{
String href = link.GetAttribute("href");
// do something with href
}

Get Search Queries from UrlReferer

I'm developing a website in ASP.Net 4. One of the requirements is to log search queries that people use to find our website. So, assuming that a URL parameter named "q" is present in Referrer, I've written the following code in my MasterPage's Page_Load:
if (!CookieHelper.HasCookie("mywebsite")) CookieHelper.CreateSearchCookie();
And my CookieHelper class is like this:
public class CookieHelper
{
public static void CreateSearchCookie()
{
if (HttpContext.Current.Request.UrlReferrer != null)
{
if (HttpContext.Current.Request.UrlReferrer.Query != null)
{
string q = HttpUtility.ParseQueryString(HttpContext.Current.Request.UrlReferrer.Query).Get("q");
if (!string.IsNullOrEmpty(q))
{
HttpCookie adcookie = new HttpCookie("mywebsite");
adcookie.Value = q;
adcookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Current.Response.Cookies.Add(adcookie);
}
}
}
}
public static bool HasCookie(string cookiename)
{
return (HttpContext.Current.Request.Cookies[cookiename] != null);
}
}
It seems ok at the first glance. I created a page to mimic a link from Google and worked like a charm. But it doesn't work on the host server. The reason is that when you search blah blah you see something like www.google.com/?q=blah+blah in your browser address bar. You expect clicking on your link in the results, will redirect to your site and you can grab the "q" parameter. But ,unfortunately, it is not true! Google, first redirects you to an address like:
http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCgQFjAA&url=http%3A%2F%2Fwww.mywebsite.com%2F&ei=cks5Uof4G-aX0QXKhIGoCA&usg=AFQjCNEdmmYFpeRRRBiT_MGH5a1x9wUUlg&bvm=bv.52288139,d.d2k&cad=rja
and this will redirect to your website. As you can see the "q" parameter is empty this time! And my code gets an empty string and actually doesn't create the cookie (or whatever).
I need to know if there is a way to solve this problem and get the real "q" value. The real search term user typed to find my website. Does anybody know how to solve this?
Google stopped passing the search keyword:
http://www.searchenginepeople.com/blog/what-googles-keyword-data-grab-means-and-five-ways-around-it.html

How to verify a Hyperlink exists on a webpage?

I have a need to verify a specific hyperlink exists on a given web page. I know how to download the source HTML. What I need help with is figuring out if a "target" url exists as a hyperlink in the "source" web page.
Here is a little console program to demonstrate the problem:
public static void Main()
{
var sourceUrl = "http://developer.yahoo.com/search/web/V1/webSearch.html";
var targetUrl = "http://developer.yahoo.com/ypatterns/";
Console.WriteLine("Source contains link to target? Answer = {0}",
SourceContainsLinkToTarget(
sourceUrl,
targetUrl));
Console.ReadKey();
}
private static bool SourceContainsLinkToTarget(string sourceUrl, string targetUrl)
{
string content;
using (var wc = new WebClient())
content = wc.DownloadString(sourceUrl);
return content.Contains(targetUrl); // Need to ensure this is in a <href> tag!
}
Notice the comment on the last line. I can see if the target URL exists in the HTML of the source URL, but I need to verify that URL is inside of a <href/> tag. This way I can validate it's actually a hyperlink, instead of just text.
I'm hoping someone will have a kick-ass regular expression or something I can use.
Thanks!
Here is the solution using the HtmlAgilityPack:
private static bool SourceContainsLinkToTarget(string sourceUrl, string targetUrl)
{
var doc = (new HtmlWeb()).Load(sourceUrl);
foreach (var link in doc.DocumentNode.SelectNodes("//a[#href]"))
if (link.GetAttributeValue("href",
string.Empty).Equals(targetUrl))
return true;
return false;
}
The best way is to use a web scraping library with a built in DOM parser, which will build an object tree out of the HTML and let you explore it programmatically for the link entity you are looking for. There are many available - for example Beautiful Soup (python) or scrapi (ruby) or Mechanize (perl). For .net, try the HTML agility pack. http://htmlagilitypack.codeplex.com/

Categories

Resources