I want to validate that an expectedresult is on this page, but I keep getting an error message. Any help is appreciated.
Here is the code I am running:
string ActualResult;
string ExpectedResult = "nH-brand-logo";
IWebDriver driver = new ChromeDriver();
driver.Navigate().GoToUrl(" ");
driver.Manage().Window.Maximize();
driver.FindElement(By.Id("user_email")).SendKeys(" ");
driver.FindElement(By.Id("user_password")).SendKeys(" ");
driver.FindElement(By.Id("user_submit")).Click();
ActualResult = driver.FindElements(By.ClassName("nH-brand-logo"));
if (ActualResult.Contains(ExpectedResult))
{
Console.WriteLine("Test Case Passed");
}
else
{
Console.WriteLine("Test Case Failed");
}
driver.Close();
driver.Quit();
}
}
}
The code you’ve posted won’t compile, because you’ve declared ActualResult as a variable of type string, and FindElements returns a ReadOnlyCollection<IWebElement>, which is most decidedly not a string.
If you’re looking to find any element on the page where the class attribute contains "nH-brand-logo", then the following would be sufficient:
var elements = driver.FindElements(By.ClassName("nH-brand-logo"));
bool testPassed = elements.Count > 0;
By calling the FindElements method in this way, if there are any elements returned, they each must, by definition, contain that value in their class attribute. Now, if you’re looking to validate something more complex than this, the full solution may be proportionally more complex.
Things to remember when working with Selenium:
FindElement finds the element or throws an exception. If the element doesn’t exist, it does not return null or an empty value; it throws.
FindElements returns all matching elements, or an empty list when no elements meet the find criteria. However, you do have to access the elements in the returned list to get information about any single element found.
The line
ActualResult = driver.FindElements(By.ClassName("nH-brand-logo"));
will return a list of webElements, which you are then validating against a string. Hence the error.
string ActualResult;
string ExpectedResult = "nH-brand-logo";
if (ActualResult.Contains(ExpectedResult))
if you want to check if all the elements for ActualResults contains 'nH-brand-logo', then you can write
bool result = ActualResults.All(x=>x.GetAttribute("class").Contains('nH-brand-logo');
Hope this helps.
Related
So HI! I'm new to C# (started about 3 weeks ago) and have taken up some programming courses and have run in to a problem.
Here is the for loop that I'm using, and I understand that the if statement looks contrary from what I wanna achieve, but when 'if (ruckSack[i] != "")' I got the "assigned string".
Furthermore, with the current code, if I just "add" an empty string to the array the it's assigned and I understand that I give the array an empty sting as a value.
int i = container;
while (i < 5)
{
if (ruckSack[i] == "")
{
Console.WriteLine("Assigned string");
i++;
}
else
{
Console.WriteLine("Empty string");
}
ruckSack[i] = Console.ReadLine();
i++;
}
So I have a display elements of array, and delete elements from array.
So I know that everything works except for the storage-func.
I would have used a list, but the assignment asks for a string array.
How would one do to solve this?
Thanks in advance!!
Best regards
EDIT: If any one wants the whole source code, tell me and i'll add it!
If yout want to avoid that the user can type a input if there is already any string in the array, yout need to inclue the Console.ReadLine() into the else-Block.
Also, you will need to use another way to check if a string is assigned as ruckSack[i] == "" only return true if the array contains an emtpy string (""). You can use string.IsNullOrEmpty(ruckSack[i]) or string.IsNullOrWhitespace(ruckSack[i]) (last one returns also true if only space on other white-spaces are in the string).
Put together:
int i = container;
while (i < 5)
{
if (string.IsNullOrWhiteSpace(ruckSack[i]))
{
Console.WriteLine("Assigned string");
i++;
}
else
{
Console.WriteLine("Empty string");
ruckSack[i] = Console.ReadLine();
}
i++;
}
I have put together the following method:
public static bool compareTableRow(List<string> expected, int rowNumberOfElemets, IWebDriver driver)
{
List<string> actual = new List<string>
{ };
for (int i = 1; i < rowNumberOfElemets + 1; i++)
{
actual.Add(driver.FindElementHighlight
(By.XPath("//*[#id=\"nefi-content\"]/div[2]/section/div/table/tbody/tr[1]/td[" + i + "]/div/input")).GetAttribute("value"));
}
if (expected.SequenceEqual(actual)) return true;
else
return false;
}
At the moment the 'expected' List is hard-coded. What kind of method variable should I put as an input to be able to call the method and pass the strings I'm trying to compare ("bla1","123", "bla2", "etc", "etc") ?
Even with your implementation you dont need to hardcode the expected argument, it is easy to call your method like this:
compareTableRow(new List<string> {"bla1", "123", "bla2", "etc", "etc"}, 42, driver);
Alternatively, you can use params keyword (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/params):
public static bool compareTableRow(int rowNumberOfElemets, IWebDriver driver, params string[] expected)
{
...
}
and call it like you described:
compareTableRow(42, driver, "bla1", "123", "bla2", "etc", "etc");
So you have an IWebDriver, which has a notion of a number of rows. You also have an input rowNumberOfElements and a list of expected strings.
You want to check if the first rowNumberOfElements items that you can FindByElementHighlight from the IWebDriver is exactly equal to the list of expected strings.
I see several issues here:
First of all: is the order in the list of expected strings important? So should the first expected string equal the first FindByElementHighLight? According to your code the order is important.
Furthermore: is the list of expected strings long enough? Can it be longer than rowNumberElements, or is it normally exactly long enough, and can you replace the rowNumberElements with the number of elements in the expected list?
If the first element does not match, you know it's useless to check the other elements, because you already know that the return value will be false.
A generic solution
I'll use this to write it as an extension method of IWebDriver. It will be as if you ask the IWebDriver: do the first X elements of you equal the first X elements of this sequence? See extension methods demystified
static bool HasExpectedHighLights(this IWebDriver driver, ICollection<string> expected)
{
// TODO: driver == null? expected == null?
return driver.HasExpectedHighLights(expected, expected.Count);
}
static bool HasExpectedHighLights(this IWebDriver driver,
IEnumerable<string> expected, int nrOfElements)
{
// TODO: driver == null? expected == null? nrOfElements < 0?
your code goes here
}
First we'll show the usage:
List<string> expectedHighLights = ...
IWebDriver driver = ...
var isExpected = driver.HasExpectedHighLights(expectedHighLights);
But is will also work in the middle of some LINQ statement:
IEnumerable<image> images = ...
var imagesWithExpectedHighLights = images
.Where(image => image.Driver.HasExpectedHighLights(expectedHighLights))
.Slect(image => ...);
So now the code. The most important part is that we should stop as soon as you can.
static string FindElementHighLight(this IWebDriver driver, int i)
{
return driver.FindElementHighlight(By.XPath("//*[#id=\" ... etc);
}
static bool HasExpectedHighLights(this IWebDriver driver,
IEnumerable<string> expected, int nrOfElements)
{
// TODO: exception if driver == null? expected == null? nrOfElements < 0?
IEnumerable<string> actualHighLights = driver
.Select( (driver, i) => driver.FindElementHighLight(i)
.Take(nrOfElements);
// note: this has only created the enumerable.
// The sequence has not been enumerated yet
return actualHighLights.SequenceEqual(expected);
// sequence equal will stop as soon as a mismatch has been found
}
So if you find a mismatch at i == 3, then HighLight(4) is not calculated.
The nice thing is that without changing your IWebDriver, you have added functionality to it: FindElementHighLight(int).
This can be used to create the IEnumerable in SequenceEqual, which prevents having to fetch all highlights, even if you detected that the first highlight was not as expected.
To collect some test data for a neural net in C# I want to use Selenium to scrape some dynamically generated data WSJ. There is an example implementation on the Selenium site that seems to do exactly what I need Finding all the input elements to the every label on a page. The example searches on TagName, I search on ClassName, but other than that, I think it's identical.
Yet, when I run this code, creating an IList with IWebElements works, but the following IJavaScriptExecutor throws an Invalid Cast exception:
Unable to cast object of type
System.Collections.ObjectModel.ReadOnlyCollection 1[System.Object]
to type
System.Collections.Generic.IList 1[OpenQA.Selenium.IWebElement]
Here's a bit of the code, this is for "text", I do the same for "num":
// Find elements by class name
IList<IWebElement> labels = driver.FindElements(By.ClassName("text"));
// get all input elements for every class label
IList<IWebElement> labVals = (IList<IWebElement>)((IJavaScriptExecutor)driver).ExecuteScript(
"var labels = arguments[0], labVals = []; for (var i=0; i < labels.length; i++){" +
"labVals.push(document.getElementById(labels[i].getAttribute('for'))); } return labVals;", labels);
I have looked at this question Selenium Web Driver C# InvalidCastException which may point to the same problem, but I don't see how the answers provided can help me.
An option could be to break up the IJavaScriptExecutor statement into "discrete" code with a work around, but I would not know how to do that.
Once I have the text labels and data values both in a List structure, I should be able to find the numbers I need.
This is not using javascript, but it will work.
I would use a CssSelector method that receives throught parameters which column/row you need, and then you would be calling this method with loop to get all info from the page.
Checking the css of the page this is what I get from the first column/row
table.mdcTable > tbody > tr:nth-of-type(3) > td:nth-of-type(1)
So, the number "3" is related to the first row, and "1" is the first column. So we can make a method to return the exact element you want:
public IWebElement test(int line, int row)
{
return driver.FindElement(By.CssSelector(string.Format("table.mdcTable > tbody > tr:nth-of-type({}) > td:nth-of-type({})", line + 2, row)));
}
Calling this method will return the element that has the text, so all you need to do is use 'element.Text' to the the value of the 'cell', or make the method return the text directly.
public String test(int line, int row)
{
return driver.FindElement(By.CssSelector(string.Format("table.mdcTable > tbody > tr:nth-of-type({}) > td:nth-of-type({})", line + 2, row))).Text;
}
The only problem would be with the columns "Latest", because they do not only contain the numbers, but a bar. You would have to create a method to take care only of these column.
This would end up with something like this:
try
{
int line = 1;
int column = 1;
while(column <= 7)
valueOfTheCell = test(line, column);
getLatestGreen(line); //string.Format("tbody > tr:nth-of-type({0}) > td:nth-of-type(9) > span.text", line)
getLatestRed(line); //string.Format("tbody > tr:nth-of-type({0}) > td:nth-of-type(8) > span.text > b", line)
}
catch (NoSuchElementException)
{
//Exception will be thrown when the code reaches the end of the list
}
I won't say this is optimal, but it is an option.
If you want to do this way I can help you with any question or problem about how to use the selector.
The cast error occurs because the IJavascriptExecutor outputs the general System.Object class MSDN which I then try to cast to an IWebElement. This may work in some cases, but in this case it does not. Changing the receiving IList to IList<Object> solves the cast exception. With this the code runs, and then I found out with the debugger that all the data is captured with the first part of the code in the Labels list. The IJavaScriptExecutor returns null items only. So the second step is not required in my case.
Input string was not in correct form.
I'm getting an exception on runtime as "System.FormatException".
Follwing lines shows exception-
public int Task
{
get
{
return Int32.Parse(TaskText.Text);
}
set
{
TaskText.Text = value.ToString();
}
}
public int Project
{
get
{
return Int32.Parse(ProjectText.Text);
}
set
{
ProjectText.Text = value.ToString();
}
}
I also tried -
Convert.ToInt32(TaskText.Text)
Convert.ToInt32(ProjectText.Text)
I need to pass these to following constructor,
Harvest_TimeSheetEntry entry = new Harvest_TimeSheetEntry(client,starttime,stoptime,task,project);
this constructor is stored in some class with task and project as integer parameters. And I can't change it because if i changed, it affects other code.
It looks as though you're getting your input from controls accepting user input, which is just asking for failure, since a user can potentially enter something that doesn't represent an integer value. You can use TryParse to avoid this:
var result = 0;
if (int.TryParse(TaskText.Text, out result)) {
return result;
}
return 0;
So, if the value of TaskText.Text == "1", this will succeed; if the value of TaskText.Text == "aaaa", this will fail - and return zero. You example would raise the appropriate exception, as experienced.
However, an exception might be the right thing to happen here, if you can't handle a bad value, don't have an alternative, and the application relies on the input to move forward. More likely, you could do with some validation on your input fields to prevent bad data being submitted.
Since your Harvest_TimeSheetEntry constructor expects task and project to be integers, you must have a list of integers that correspond to the different tasks and projects. Now you can't expect Int32 to know which task corresponds to which number, can you?
I would suggest you use ComboBoxes for TaskText and ProjectText. Then, you can assign the correct corresponding integer to each ComboBoxItem.Tag.
Please note that this goes far beyond the kind of answers you should expect from SO.
if you do not use MVVM or binding you can simply do the check before your need it. t
int task;
int project;
if(!Int32.TryParse(TaskText.Text, out task))
{} //errorhandling here
if(!Int32.TryParse(ProjectText.Text, out project))
{}//errorhandling here
//all fine
var entry = new Harvest_TimeSheetEntry(client,starttime,stoptime,task,project);
You must check if you can parse it into Integer
try
Int32 foo =0;
if (Int32.TryParse(TaskText.Text, out foo))
{
return foo;
}
I love LINQ statements for the expressive syntax and other convenient features. However, I find it very troublesome to debug them sometimes. Specifically, when I run a LINQ statement on a collection and one of the elements in the collection causes an exception, how can I figure out what the problem input was and where the problem came from?
Imagine I have a text file with 1000 real numbers:
0.46578
12.314213
1.444876
...
I am reading this as a List<string> and loading it into a more specific data structure:
var file_contents = File.ReadAllLines("myfile.txt");
var data = file_contents.Select(s => double.Parse(s));
Now, for this particular input, I didn't bother to look at it carefully and it turns out the 876th line contains (line numbers shown):
875 5.56786450
876 Error: Could not calculate value.
878 0.0316213
For whatever reason (perhaps the file was generated by a script that malfunctioned). My LINQ method chain will of course throw an exception. The problem is, how do I figure which element of the list caused the exception, and what its value was?
To clarify, if instead I used a for-loop:
var data = new List<double>();
foreach(var row in file_contents)
{
var d = double.Parse(row);
data.Add(d);
}
Then the exception would highlight the string which calls double.Parse, and I would be able to mouse over row to easily see what the problem input was.
I can, of course, use Resharper to convert my LINQ statements into for-loops, and then debug them, but is there a better way?
Put a conditional breakpoint on the lambda function, where the condition is s.StartsWith("5.56"). You just need to have your cursor on the lambda and press F9. Assuming you're using visual studio.
var data = file_contents.Select(s => {
try
{
return double.Parse(s);
}
catch
{
throw; //breakpoint?
}
});
Disclaimer: I work for OzCode
LINQ debugging is hard borderline impossible using Visual Studio. I suggest you try using OzCode.
This is what your code looks when debugging (the exception in on the 6th item).
You can tell which item caused the exception by investigating the items that where passed to the Select clause - and since the last one triggered the exception - it's easy to find the offending value.
If you're interested you can try OzCode's LINQ debugging - we've just started an EAP
I would just use a tryparse personally.
var data = new List<string>
{
"0.46578",
"12.314213",
"Error: Could not calculate value.",
"1.444876",
};
double d;
var good = data.Where(s => Double.TryParse(s, out d)).Select(Double.Parse);
var bad = data.Where(s => !Double.TryParse(s, out d)).Select(x => new
{
key = data.IndexOf(x),
value = x
}).ToDictionary(x => x.key, x => x.value);
textBox1.AppendTextAddNewLine("Good Data:");
WriteDataToTextBox(good);
textBox1.AppendTextAddNewLine(String.Format("{0}{0}Bad Data:", Environment.NewLine));
WriteDataToTextBox(bad);
The AppendTextAddNewLine is simply an extension method I wrote for my little proof of concept test program
public static void AppendTextAddNewLine(this TextBox textBox, string textToAppend)
{
textBox.AppendText(textToAppend + Environment.NewLine);
}
Edit
The WriteDataToTextbox is a generic method that writes an IEnumerble<T> out to the text box.
void WriteDataToTextBox<T>(IEnumerable<T> data )
{
foreach (var row in data)
{
textBox1.AppendTextAddNewLine(row.ToString());
}
}
Forgot to put the output here so I figure I should do that. It shows the index of the bad data and the data itself that caused the problem.
Good Data:
0.46578
12.314213
1.444876
Bad Data:
[2, Error: Could not calculate value.]
I'm not sure why you don't like foreach loop here. LINQ uses it internally anyway, and as you've already realized there are some pros and cons of using LINQ and debugging is one of cons.
I would probably mix LINQ with foreach and end up with following:
// read all lines from file //
var file_contents = File.ReadAllLines("myfile.txt");
// set initial data list length to number of lines for better performance
var data = new List<double>(file_contents.Length);
// list for incorrect line numbers
var incorrectRows = new List<int>();
foreach (var x in file_contents.Select((s, i) => new {s, i}))
{
// x.s - line string
// x.i - line number
double value;
if (double.TryParse(x.s, out value))
data.Add(value); // add value, which was OK
else
incorrectRows.Add(x.i); // add index of incorrect value
}
That will prevent an exception at all and will give you line numbers for all incorrect values. It also iterate over file_contents just once and every value is being parsed only once.