How to wait until div dropdown is loaded using selenium webdriver? - c#

There are two dropdown menus on a page.
When i click on First dropdown, then in second dropdown options will be automatically loaded after couple of seconds.
Here comes my problem, that how can i wait for the second dropdown to load fully using selenium webdriver.
<div name="ddlFruit_N" id="ddlFruit_N" class="Searchddl">
<div class="chosen-drop">
<div class="chosen-search">
<input class="chosen-search-input" type="text" autocomplete="off">
</div>
<ul class="chosen-results">
<li class="active-result" data-option-array-index="0">Select</li><li class="active-result" data-option-array-index="1">Apple</li><li class="active-result" data-option-array-index="2">Mango</li><li class="active-result" data-option-array-index="3">Grapes</li><li class="active-result" data-option-array-index="4">Banana</li><li class="active-result" data-option-array-index="5">Guava</li>
</div>
Note: This is div dropdown
Second dropdown list xpath: //*[#id='ddlFruit_N']/div/ul/li
Am using C# Thank you

Please try below code. We will check for options to load in Div
public static IWebElement waitForDropdownPopulate(IWebDriver driver, By by, int delayInSeconds)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(delayInSeconds));
return wait.Until<IWebElement>(drv =>
{
IWebElement elementList = drv.FindElement(by);
elementList.Click();
if (elementList.FindElements(By.XPath("./div/ul//li")).Count >= 2)
{
return elementList;
}
return null;
}
);
}
and I call it like this:
Myclass.waitForDropdownPopulate(driver, By.XPath("//*[#id='ddlFruit_N']"), 30);

Try below code
var items = wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.XPath("//*[#id='ddlFruit_N']/div/ul/li")))
.FindElements(By.XPath("//*[#id='ddlFruit_N']/div/ul/li"));
To use seleniumExtras library install DotNetSeleniumExtras.WaitHelpers from NugetPackage

Related

Cannot automate signing in to Azure Active Directory using Selenium WebDriver

I'm trying to test logging in to our web application using Azure AD but the click action on the Sign In button doesn't seem to register. Here's the code:
namespace WebApp.Tests
{
using System;
using System.IO;
using System.Threading;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
[TestClass]
public class LoginTests
{
[TestMethod]
public void CanLogin()
{
string internetExplorerDriverServerDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
IWebDriver driver = new InternetExplorerDriver(internetExplorerDriverServerDirectory)
{
Url = "https://localhost:44399"
};
try
{
driver.Manage().Cookies.DeleteAllCookies();
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
wait.Until(d => d.FindElement(By.Id("logonIdentifier")));
driver.FindElement(By.Id("logonIdentifier")).SendKeys("username");
driver.FindElement(By.Id("password")).SendKeys("password");
driver.FindElement(By.Id("next")).Click();
wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
wait.Until(d => d.FindElement(By.ClassName("logo_title")));
driver.FindElement(By.ClassName("logo_title")).Text.Should().Contain("HELLO!");
}
finally
{
driver.Close();
driver.Quit();
driver.Dispose();
driver = null;
}
}
}
}
Here's the relevant bits of HTML:
<div class="entry">
<div class="entry-item">
<label for="logonIdentifier">Email Address</label>
<div class="error itemLevel" aria-hidden="true" style="display: none;">
<p role="alert"></p>
</div>
<input type="email" id="logonIdentifier" name="Username or email address" pattern="^[a-zA-Z0-9.!#$%&’'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" placeholder="Email Address" value="" tabindex="1" autocomplete="off">
</div>
<div class="entry-item">
<div class="password-label">
<label for="password">Password</label>
<a id="forgotPassword" tabindex="2" href="/redacted">Forgot your password?</a>
</div>
<div class="error itemLevel" aria-hidden="true" style="display: none;">
<p role="alert"></p>
</div>
<input type="password" id="password" name="Password" placeholder="Password" tabindex="1" autocomplete="off">
</div>
<div class="working"></div>
<div class="buttons">
<button id="next" tabindex="1">Sign in</button>
</div>
</div>
It all looks OK in the browser: the username and password fields are filled in, the button looks like it gets clicked (the little "working" icon appears briefly above it) but nothing happens.
I've tried a longer wait (up to 30 seconds) for the home page to
appear after clicking the Sign In button.
wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
I've tried sending an Enter key while in the form instead of clicking
the button.
driver.FindElement(By.Id("password")).SendKeys(Keys.Enter);
I've tried executing some JavaScript to call the button click action.
IJavaScriptExecutor jse = (IJavaScriptExecutor)driver;
jse.ExecuteScript("document.getElementById('next').click();");
I've tried using the Chrome and Firefox drivers.
I've tried on both the local development version and the hosted test environment.
Nothing so far has worked. There are no error/validation messages shown in the browser during the process.
I'm at a bit of a loss with this one.
Thanks,
Stuart.
As of 2019-01-20, this is working for me:
[ClassInitialize]
public static void InitializeClass(TestContext testContext)
{
var options = new ChromeOptions();
options.AddArguments("--incognito");
driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), options);
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
}
[TestMethod]
public void Login()
{
driver.Navigate().GoToUrl("https://localhost:44399/");
driver.FindElement(By.Id("i0116")).Clear();
driver.FindElement(By.Id("i0116")).SendKeys("test#test.onmicrosoft.com");
driver.FindElement(By.Id("i0116")).SendKeys(Keys.Enter);
driver.FindElement(By.Id("i0118")).Clear();
driver.FindElement(By.Id("i0118")).SendKeys("password");
Thread.Sleep(500);
driver.FindElement(By.Id("i0118")).SendKeys(Keys.Enter);
driver.FindElement(By.Id("idSIButton9")).Click();
}
In My Case, My app uses the Azure AD popup Authentication
in my initial case, it was not able to find the elements in the Popup
var userNameField = GetElementAfterWait(By.Id("i0116"));
after adding the below code, I am able to access the fields
driver.SwitchTo().Window(driver.WindowHandles[1]);
** GetElementAfterWait this is our private method to get the element after some wait time
private static IWebElement GetElementAfterWait(By by, bool isWaitRequired = true)
{
if (isWaitRequired)
{
WebDriverWait waitForElement = new WebDriverWait(driver, TimeSpan.FromSeconds(60));
}
return driver.FindElement(by);
}
I have solved the same by using CSS selector as element locator.
SeleniumLibrary.Input Text css:#i0118 your_password
it's working fine in every iteration.

How to count nested div using selenium c#?

<div class="bodyCells">
<div style="position:absolute;left:0;">
<div style="overflow:hidden;">
<div title="AAA" class="pivotTableCellWrap">AAA</div>
<div title="BBB" class="pivotTableCellWrap">BBB</div>
</div>
<div>
<div title="AAA-123" class="pivotTableCellWrap">AAA-123</div>
<div title="BBB-123" class="pivotTableCellWrap">BBB-123</div>
</div>
</div>
</div>
I have two bodycells div in my page and I want the count the nested div inside the second one.
Required output :- I want the count=2
Tried Approach :-
int rowCount = driver.FindElements(By.XPath("//div[#class='bodyCells[2]']//div").Count());
Console.WriteLine(rowCount);
you can use the below modified XPath inorder to get the count of second nested div
XPath: //div[#class='bodyCells']/div/div[2]/div
Code:
var rowCount = _driver.FindElements(By.XPath("//div[#class='bodyCells']/div/div[2]/div")).Count;
Console.WriteLine(rowCount);
As per the HTML you have provided to count the nested child <divs> inside the second (parent) <div> you can use either of the following solution:
CssSelector:
List<string> elements = driver.FindElements(By.CssSelector("div.bodyCells div.pivotTableCellWrap[title*='-']"));
Console.WriteLine(elements.Count);
XPath:
List<string> elements = driver.FindElements(By.XPath("//div[#class='bodyCells']//div[#class='pivotTableCellWrap' and contains(#title,'-')]"));
Console.WriteLine(elements.Count);

Selenium clicking button hidden by span

I am trying to automate an environment selection screen where there are multiple selectable buttons individually hidden by a span, these display as tiles.
I have managed to navigate to a given tile and pull up the button but I am unable to click it.
Here is the code I have
public static void NavigateToEnvironment(IWebDriver driver, string environment)
{
IWait<IWebDriver> wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5.00));
wait.Until(ExpectedConditions.ElementIsVisible(By.XPath($"//span[text()='{environment}']")));
var tile = driver.FindElement(By.XPath($"//span[text()='{environment}']"));
Actions action = new Actions(driver);
action.MoveToElement(tile).Perform();
wait.Until(ExpectedConditions.ElementIsVisible(By.XPath($"//*[#span=(text()='{environment}')][#btn=(starts-with(text(), 'Start'))]")));
driver.FindElement(By.XPath($"//*[starts-with(text(), 'Start')]")).Click();
}
The first part successfully moves to the correct tile and opens the span so on screen the button is there.
The wait.until condition is fine too so Selenium can see the element so its the final click command I have an issue with.
It seems only to look for the button hidden within tile one but I am trying tile three. All the buttons have the same HTML tags.
In the current code state I get element not visible.
I have tried to use the xpath as in the wait condition but that returns that the parameters are not elements so again fails.
I am kind of at a loss. Any ideas?
UPDATE:
Some HTML of one of the buttons. This basically repeats with a different application name
<li class="trans tile">
<div class="tileWrap noselect" aria-haspopup="true">
<div class="divNavIcon">
<span class="spnNavIcon primarycolorfont enable" data-bind="css: Code"></span>
</div>
<div class="tilePopup primarycolor">
<span data-bind="text: ApplicationNameAlias ? ApplicationNameAlias : ApplicationName">Enable QA</span>
<span data-bind="text: Description" class="tileSubText">Enable CI Environment</span>
<div class="tilePopupToggle">
<button type="button" data-bind="click: $parent.startApp, css: { disabled: IsRevoked }" class="btn">Start <i class="fa fa-fw fa-desktop"></i></button>
<button type="button" style="display:none;" data-bind="click: $parent.startAppNew, css: { disabled: IsRevoked }" class="btn">Start New <i class="fa fa-fw fa-external-link"></i></button>
<button type="button" style="display:none;" data-bind="attr: { "data-target": "#appPreview_" + ApplicationID }" class="btn" data-toggle="modal" data-target="#appPreview_3043">Preview <i class="fa fa-fw fa-play"></i></button>
</div>
</div>
</div>
Screenshot to help understanding - Each tile acts in the same way with a hidden start button. My code works fine for this first tile but if I want the second or third tiles it cannot find the start button
As per the HTML you have shared to click on the button with text as Start you can use the following code block :
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//div[#class='tilePopup primarycolor']//div[#class='tilePopupToggle']/button[#class='btn' and normalize-space()='Start']/i[#class='fa fa-fw fa-desktop']"))).Click();
Update
Can you try removing the <button> tag as :
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//div[#class='tilePopup primarycolor']//div[#class='tilePopupToggle']//i[#class='fa fa-fw fa-desktop']"))).Click();
Note : As per aurelia/binding/issues/163 disable.bind disables button but inner content is still clickable and we are targeting i[#class='fa fa-fw fa-desktop']
I have managed a pretty elegant work around to this issue. The buttons are contained in li items so i'm just finding the relevant one of those.
public void NavigateToEnvironment(IWebDriver driver, string environment)
{
var tile = driver.FindElement(By.XPath($"//span[text()='{environment}']"),5);
Actions action = new Actions(driver);
action.MoveToElement(tile).Perform();
var tile2 = driver
.FindElement(By.XPath("//*[#id='content']/div/div/div/div/ul"))
.FindElements(By.TagName("li"))
.Where(x => !string.IsNullOrWhiteSpace(x.Text))
.ToList();
var singleTile = tile2.Single(x => x.Text.Contains(environment));
driver.FindElement(By.XPath($"//*[#id='content']/div/div/div/div/ul/li[{tile2.IndexOf(singleTile) + 1}]/div[1]/div[2]/div/button[1]")).Click();
}

Selenium C# List

Hello Stackoverflow Users,
I have a internet site with 99 list elements.
The diffrence between the elements are only the names.
<li class="_6e4x5">
<div class="_npuc5">
<div class="_f5wpw">
<div class="_eryrc">
<div class="_2nunc">
<a class="_2g7d5 notranslate _o5iw8" title="Name1" href="/"Name1/">"Name1</a>
</div>
</div>
</div>
</div>
</li>
[...]
<li class="_6e4x5">
<div class="_npuc5">
<div class="_f5wpw">
<div class="_eryrc">
<div class="_2nunc">
<a class="_2g7d5 notranslate _o5iw8" title="Name99" href="/"Name99/">"Name99</a>
</div>
</div>
</div>
</div>
</li>
What I want:
I want to take the "title" of each list element and put it in a new list.
What I tried:
List<string> following = new List<string>();
By name = By.XPath("//div[#class='_2nunc']");
IJavaScriptExecutor js = driver as IJavaScriptExecutor;
IList<IWebElement> displayedOptions = driver.FindElements(name);
foreach (IWebElement option in displayedOptions)
{
string temp = displayedOptions[i].ToString();
following.Add(temp);
i++;
}
If I run the code, I only get the element ID, and not the "title" (name34 for example). I hope you have enough information to help me with my problem. Thanks in advance for every help!
To take the title of each list element and put it in a new list you can use the following code block :
List<string> following = new List<string>();
IList<IWebElement> displayedOptions = driver.FindElements(By.XPath("//li[#class='_6e4x5']//a[#class='_2g7d5 notranslate _o5iw8']"));
foreach (IWebElement option in displayedOptions)
{
string temp = option.GetAttribute("title");
following.Add(temp);
}
You're looking to get the a element's title attribute. The selenium IWebElement interface has a GetAttribute method you can use to get the title of your elements.
foreach (IWebElement option in displayedOptions)
{
following.Add(option.GetAttribute("title"));
}

Extract text from a div

I am trying to make a DropDownList using divs and jquery (so that I can style it as I want)...and its working but the problem is I cant get the selected value from the list..
After selection of the option I am copying the selected value into the a div.. and I want to extract this using c# (in .aspx.cs page)... I've tried to do it using string builder and innerHtml(after adding runat="server" to the div).. but it doesn't work ...code is as follows
.aspx Page:
<div class="ddl">
<div id="lowertriangle" class="lowertriangle"></div>
<div id="uppertriangle" class="uppertriangle"></div>
<div id="label" class="labeldiv_dd" runat="server"></div>//***This is the div from which I want to extract value***
<div id="options" class="optionsidv_dd">
<ul id="options_ul">
<li id="0">Select One Option</li>
<li id="1">Option 1</li>
<li id="2">Option 2</li>
<li id="3">Option 3</li>
<li id="4">Option 4</li>
<li id="5">Option 5</li>
</ul>
</div>
</div>
aspx.cs page
Method 1 that I tried:
string sel_text = label.InnerHtml;
display_sel_value.Text = sel_text.ToString();
2nd method:
var sb = new StringBuilder();
label.RenderControl(new HtmlTextWriter(new StringWriter(sb)));
string s = sb.ToString();
Kindly point out my mistakes and help me in this regard(in extracting innerHTML of the div that is).
Thanks
No, putting content in a div won't work.
Your example isn't complete enough to see all that happens, but let's assume that you're in a standard <form>, you're setting the div's inner HTML to a value with Javascript and then you're submitting in the standard way.
Then one way to do what you want is to use a hidden input and setting its value attribute instead of its contents.
<input id="label" class="labeldiv_dd" runat="server" type="hidden">
In the codebehind, the C# can retrieve the value from this control after submitting with the .Value property.
Ok guys thx for your replies..I found a way around the problem...I used HiddenField control to store the selected value using jQuery like so
$("#options_ul li").click(function () {
var text = this.innerHTML;
***$('#<%= selectedvalue.ClientID %>').val(text);***
$("#options_ul li").css("background-color", "#c2c2c2");
$(this).css("background-color", "white");
//var prev = this.id;
//document.getElementById("label").innerHTML = text;
toggleHeight();
});
and then accessed it server side using
selectedvalue.value;
PS: "selectedvalue" is id of the hiddenfield control

Categories