I'm trying to use this snippet to test of an element has a specific text.
HtmlDocument element = webBrowser2.Document;
if (element.GetElementById("gbqfsa").InnerText == "Google Search")
{
HasSucceeded = 1;
}
return HasSucceeded;
However the first line throws the exception "Specified cast is not valid." What am I doing wrong?
Is it possible that you are using the wrong HtmlDocument class? WebBrowser.Document is of the type:
System.Windows.Forms.HtmlDocument
But I noticed that there is also another possible namespace:
System.Windows.Browser.HtmlDocument
I would check to make sure the namespace you included was System.Windows.Forms.HtmlDocument
I encounter this problem when return HtmlDocument as property from my custom user control. (Which embedded WebBrowser control)
Cause of error because access document from other thread.
/// <summary>
/// Error version '
/// </summary>
public HtmlDocument Document
{
get
{
// Throw error 'Specified cast is not valid'
return this.webBrowserMain.Document;
}
}
But I don't known why error is not 'CrossThread Operation access ...' but next code solved my problem
/// <summary>
/// Fixed version
/// </summary>
delegate HtmlDocument DlgGetDocumentFunc();
public HtmlDocument GetDocument()
{
if(InvokeRequired)
{
return (HtmlDocument)this.webBrowserMain.Invoke(new DlgGetDocumentFunc(GetDocument), new object[] { });
}
else
{
return this.webBrowserMain.Document;
}
}
Related
In my application, a toast message(pop up) appears when i try to enter invalid cell phone number and i want to assert the text of that toast message.
Below is the image of my HTML.I am unable to copy paste here.
Below is the code tried but no such element error occurs.
String actual_error = driver.FindElement(By.XPath("//div[#class='toast-message']")).Text;
String expected_error = "CellphoneNumber:Please enter a valid cellphone number. :";
Assert.AreEqual(actual_error, expected_error);
Console.WriteLine("Phone Number error message validated successfully");
Kindly suggest the right way to assert the toast message.
I think you can do like this:
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using Serilog;
using System;
using System.IO;
using System.Reflection;
namespace StackOverFlow.Answer.Selenium.AssertMessageText
{
class AssertMessagePhone
{
public static IWebDriver driver;
public static WebDriverWait wait;
public static int timeOut = 30;
[Test]
[Category("ClickPartilLink")]
public void AssertMessagePhoneTest()
{
string messageError = "CellphoneNumber:Please enter a valid cellphone number. :";
Log.Information("Get instance Chrome Browser");
driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), OptionsChrome());
Console.WriteLine("Acess your url site");
driver.Navigate().GoToUrl("http://YourUrlSite/index.aspx");
// Steps until to fill and submit phone
Log.Information("Wait notificantion message error");
WaiElement(By.CssSelector(".toast-message"));
Log.Information("Check message");
Assert.True(GetText(By.CssSelector(".toast-message")).ToUpper().Contains(messageError.ToUpper()));
Log.Information("Click on message to disaper");
Click(By.CssSelector("div[class='noty_bar noty_type_success'] > div > span"));
}
/// <summary>
/// Wait elements
/// </summary>
/// <param name="locator"></param>
private void WaiElement(By locator)
{
wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut));
wait.Until(condition: ExpectedConditions.PresenceOfAllElementsLocatedBy(locator));
}
/// <summary>
/// click on elements
/// </summary>
/// <param name="locator"></param>
private void Click(By locator)
{
WaiElement(locator);
driver.FindElement(locator).Click();
}
/// <summary>
/// return text from element
/// </summary>
/// <param name="locator"></param>
/// <returns></returns>
private string GetText(By locator)
{
WaiElement(locator);
return driver.FindElement(locator).Text;
}
}
}
Your verification seems fine. You need to make sure that:
- your tests are in proper order if page is opened in another one
- make sure you wait for all elements, i.e use method:
ExpectedConditions.PresenceOfAllElementsLocatedBy(/*element to test*/)
I agree with lukbl that I would create a bool to verify the element it present but I believe the issue is that your text is in a div tag and not in a span.
Try this:
String expected_error = driver.FindElement(By.XPath("//div[#class='toast-message']/text()[1]")).Text;
This is most likely a error of logic in my code.
In first place, what i'm trying to do is:
go to the respective page of my website which is in this case link and collect the data, with my public void GDataPicker.
now where i want you to help me is, i use the following code to see if the button next exists in the webpage, and collect it's respective data, but always give me the same error:
OpenQA.Selenium.StaleElementReferenceException: 'stale element reference: element is not attached to the page document
(Session info: chrome=58.0.3029.110)
(Driver info: chromedriver=2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41),platform=Windows NT 10.0.15063 x86_64)' , i think it's probably because i don´t update my NextButtonElement.
Code:
Boolean ElementDisplayed;
try
{
Gdriver.Navigate().GoToUrl("http://www.codigo-postal.pt/");
IWebElement searchInput1 = Gdriver.FindElement(By.Id("cp4"));
searchInput1.SendKeys("4710");//4730
IWebElement searchInput2 = Gdriver.FindElement(By.ClassName("cp3"));
searchInput2.SendKeys("");//324
searchInput2.SendKeys(OpenQA.Selenium.Keys.Enter);
IWebElement NextButtonElement = Gdriver.FindElement(By.XPath("/html/body/div[4]/div/div/div[2]/ul/li[13]/a"));
GDataPicker();
while (ElementDisplayed = NextButtonElement.Displayed)
{
GDataPicker();
Gdriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(2000));
NextButtonElement.SendKeys(OpenQA.Selenium.Keys.Enter);
}
}
catch (NoSuchElementException i)
{
ElementDisplayed = false;
GDataPicker();
}
I cant help you with C#, however StaleElementReferenceException occurs when the element you act upon is still in the dom but has been replaced with an identical one. what i would do is catch that exception and find the element again
catch (StaleElementReferenceException i)
{
IWebElement NextButtonElement = Gdriver.FindElement(By.XPath("/html/body/div[4]/div/div/div[2]/ul/li[13]/a"));
}
http://www.seleniumhq.org/exceptions/stale_element_reference.jsp
I would use ExpectedConditions.ElementToBeClickable with the dynamic wait feature selenium has.
var wait = new WebDriverWait(GDriver, TimeSpan.FromSeconds(5));
IWebElement NextButtonElement = wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("/html/body/div[4]/div/div/div[2]/ul/li[13]/a")));
ExpectedConditions.ElementToBeClickable does exactly what you want it to do, wait a little bit until the element is displayed and not stale.
/// <summary>
/// An expectation for checking an element is visible and enabled such that you
/// can click it.
/// </summary>
/// <param name="locator">The locator used to find the element.</param>
/// <returns>The <see cref="IWebElement"/> once it is located and clickable (visible and enabled).</returns>
public static Func<IWebDriver, IWebElement> ElementToBeClickable(By locator)
{
return (driver) =>
{
var element = ElementIfVisible(driver.FindElement(locator));
try
{
if (element != null && element.Enabled)
{
return element;
}
else
{
return null;
}
}
catch (StaleElementReferenceException)
{
return null;
}
};
}
From https://github.com/SeleniumHQ/selenium/blob/master/dotnet/src/support/UI/ExpectedConditions.cs
Which is the preferred method to open a Url (and are there any differences behind the scenes between):
driver.Url = "http://example.com";
or
driver.Navigate().GoToUrl("http://example.com");
Also, if the driver is already pointing at the same page, will setting the Url a second time cause the page to refresh?
i.e.
...
driver.Url = "http://example.com";
driver.Url = "http://example.com"; //does this reload the page?
...
FWIW I'm using the Chrome driver chromedriver.exe, but it doesn't appear to be a managed assembly (I tried opening it with ILSpy but no luck).
Selenium is an open source framework, so please have a look at the source code here.
GoToUrl() is defined in RemoteNavigator.cs:
/// <summary>
/// Navigate to a url for your test
/// </summary>
/// <param name="url">String of where you want the browser to go to</param>
public void GoToUrl(string url)
{
this.driver.Url = url;
}
/// <summary>
/// Navigate to a url for your test
/// </summary>
/// <param name="url">Uri object of where you want the browser to go to</param>
public void GoToUrl(Uri url)
{
if (url == null)
{
throw new ArgumentNullException("url", "URL cannot be null.");
}
this.driver.Url = url.ToString();
}
So basically driver.Navigate().GoToUrl(); sets driver.Url under the hood and I don't see a difference there.
However, driver.Navigate().GoToUrl() is more flexible, which allows sending either string or Uri as parameter types, while only string is allowed when setting through driver.Url.
To your second question, the source code shows that driver.Navigate().Refresh() asks browsers to refresh, while driver.Url tells browsers to navigate. So these two are fundamentally different. For more details, please see Difference between Refresh and Navigate function in browser control?
If you want to refresh the page, please use driver.Navigate().Refresh();
Refresh() is defined in RemoteNavigator.cs:
/// <summary>
/// Refresh the browser
/// </summary>
public void Refresh()
{
// driver.SwitchTo().DefaultContent();
this.driver.InternalExecute(DriverCommand.Refresh, null);
}
driver.Url is defined in RemoteWebDriver.cs:
public string Url
{
...
set
{
...
try
{
this.Execute(DriverCommand.Get, parameters);
}
...
}
}
Adding to Yi Zeng's answer, the difference between reloading and refreshing has to do with the driver information about the page. If we re-assign driver.Url = url again, the memory will host this overridden string value one more time —regardless of being the same. While refreshing has to do with displaying updated data.
I have a web service that uses a call to the System.ServiceModel.Dispatcher.SerializeReply message (specifically the MultiplexingDispatchMessageFormatter implementation). When I make this call, I am getting a "Key not found" exception that I traced to a line in System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter where it is trying to get the "defaultContentType" based on a key.
Unfortunately I'm unable to see what the key is, but the defaultContentType collection does not appear to have any items in it.
What do I need to do to get the SerializeReply working?
The code:
public System.ServiceModel.Channels.Message SerializeReply(System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters, object result)
{
System.Web.HttpRequest requestMessage = System.Web.HttpContext.Current.Request;
string format = requestMessage.QueryString["format"];
if (!string.IsNullOrEmpty(format) && string.Compare(format, "json", true) == 0)
{
return jsonResponseDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
}
//[UNRELATED CODE]
}
This is the line that's blowing up in the System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter (the key is "json", the defaultContentTypes has no entries):
outgoingResponse.ContentType = this.defaultContentTypes[key];
And the exception I'm getting:
System.Collections.Generic.KeyNotFoundException occurred
Message=The given key was not present in the dictionary.
Source=mscorlib
StackTrace:
at System.ThrowHelper.ThrowKeyNotFoundException()
InnerException:
I had this problem and figured out how to fix it for my case with good ol' Reflector. The reason was because the ContentType of the OutgoingResponse was empty. Adding the following immediately before you call "SerializeReply" against your jsonResponseDispatchMessageFormatter might do the trick:
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
The accepted answer works. However, if you want your endpoint to support XML and JSON, depending on which attribute is configured, below is code that is more robust for either scenario.
/// <summary>
/// Try to set the content type as close to the user's preference as we can get.
/// </summary>
private void SetContentType()
{
if (WebOperationContext.Current == null)
{
throw new InvalidOperationException("There is no WebOperationContext. This class expects to operate within a WCF context.");
}
WebMessageFormat contentType = WebMessageFormat.Json;
//first let's grab the default, if possible
if (_serviceEndpoint.EndpointBehaviors.Contains(typeof (WebHttpBehavior)))
{
var behavior = (WebHttpBehavior)_serviceEndpoint.EndpointBehaviors[typeof (WebHttpBehavior)];
contentType = behavior.DefaultOutgoingResponseFormat;
}
else if (_serviceEndpoint.EndpointBehaviors.Contains(typeof(WebHttpByteResponseBehavior)))
{
var behavior = (WebHttpByteResponseBehavior)_serviceEndpoint.EndpointBehaviors[typeof(WebHttpByteResponseBehavior)];
contentType = behavior.DefaultOutgoingResponseFormat;
}
//then let's see if an explicit override is available
if (_operationDescription.OperationBehaviors.Contains(typeof (WebInvokeAttribute)))
{
var behavior = (WebInvokeAttribute)_operationDescription.OperationBehaviors[typeof(WebInvokeAttribute)];
if (behavior.IsResponseFormatSetExplicitly)
{
contentType = behavior.ResponseFormat;
}
}
else if (_operationDescription.OperationBehaviors.Contains(typeof(WebGetAttribute)))
{
var behavior = (WebGetAttribute)_operationDescription.OperationBehaviors[typeof(WebGetAttribute)];
if (behavior.IsResponseFormatSetExplicitly)
{
contentType = behavior.ResponseFormat;
}
}
//finally set the content type based on whatever we found
WebOperationContext.Current.OutgoingResponse.ContentType = MapToStringContentType(contentType);
}
/// <summary>
/// Maps from a WebMessageFormat to a valid Content-Type header value.
/// </summary>
/// <param name="contentType"></param>
/// <returns></returns>
private string MapToStringContentType(WebMessageFormat contentType)
{
switch (contentType)
{
case WebMessageFormat.Xml:
return TEXT_XML;
break;
case WebMessageFormat.Json:
return APPLICATION_JSON;
break;
default:
return APPLICATION_JSON;
break;
}
}
Then, just make sure to call SetContentType() before you call SerializeReply(...). This is mainly applicable if you are calling one of the built-in WCF formatters in a scenario where you're sometimes calling your own formatter, otherwise calling a built-in WCF formatter.
WatiN.Core.IE window = new WatiN.Core.IE();
// Frames
// Model
TextField txt_txtName = window.TextField(Find.ByName("txtName"));
TextField txt_txtPassword = window.TextField(Find.ByName("txtPassword"));
Button btn_btnLogin = window.Button(Find.ByName("btnLogin"));
// Code
window.GoTo("http://134.554.444.55/asdfgfghh/");
txt_txtName.TypeText("fghfjghm");
txt_txtPassword.TypeText("gfhgjfgh");
btn_btnLogin.Click();
}
only the window.GoTo("http://134.554.444.55/asdfgfghh/"); code works and the rest are doing nothing,
When I am using a catch block it throws exception as
Could not find INPUT (hidden) or INPUT (password) or INPUT (text) or INPUT (textarea) or TEXTAREA element tag matching criteria: Attribute 'name' equals 'txtName' at "http://134.554.444.55/asdfgfghh/ (inner exception: Unable to cast COM object of type 'System.__ComObject' to interface type 'mshtml.IHTMLElement'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}' failed due to the following error: Error loading type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)).)
My answer is very similar with Pavlo's one, but I must advice to use Page which is a built-in suport for Model as described by Pavlo.
Create a class MyPage
public class MyPage : WatiN.Core.Page
{
public TextField NameField
{
get { return Document.TextField(Find.ByName("txtName")); }
}
public TextField PasswordField
{
get { return Document.TextField(Find.ByName("txtPassword")); }
}
public TextField LoginButton
{
get { return Document.Button(Find.ByName("btnLogin")); }
}
}
Then you can just call .Page<MyClass> method.
using(var browser = new IE("http://134.554.444.55/asdfgfghh/"))
{
var page = browser.Page<MyClass>();
page.NameField.TypeText("name field");
page.PasswordField.TypeText("password field");
page.LoginButton.Click();
}
When you call Button, TextField or whatever it does not create mapping it actually searches for control on page. And if the page is not opened yet than control does not exist.
You can create properties that will find control when you request it. So you define a particular model as class with appropriate properties.
public TextField txt_txtName
{
get
{
return window.TextField(Find.ByName("txtName"));
}
}
Added: If creating properties does not work for you, then use this:
var model = new
{
txt_txtName = new Func<TextField>(() => window.TextField(Find.ByName("txtName"))),
txt_txtPassword = new Func<TextField>(() => window.TextField(Find.ByName("txtPassword"))),
btn_btnLogin = new Func<Button>(() => window.Button(Find.ByName("btnLogin")))
};
window.GoTo("http://134.554.444.55/asdfgfghh/");
model.txt_txtName().TypeText("fghfjghm");
model.txt_txtPassword().TypeText("gfhgjfgh");
model.btn_btnLogin().Click();