I use SpecFlow with Coded UI to create some automated functional tests for a WPF application.
Let's see the following SpecFlow scenario:
Scenario: Issue
When Results button is pressed
When Result 123 is selected
When Results button is pressed
When Result 123 is selected
1st and 3rd row parameter: Results
2nd and 4th row parameter: 123
Here are the methods for the upper mentioned steps:
When Results button is pressed
public void PressButton(string buttonName)
{
WpfButton uIButton = this.UIAUT.UIButton;
uIButton.SearchProperties[WpfButton.PropertyNames.Name] = buttonName;
uIButton.WaitForControlEnabled();
Mouse.Click(uIButton);
}
When Result 123 is selected
public void SelectResultByID(string resultId)
{
WpfListItem uIResult = this.UIAUT.UITier1List.UITier2ListBox.UITier3ListItem;
var allResults = uIResult.FindMatchingControls();
foreach (WpfListItem item in allResults)
{
string[] elem = item.GetChildren().GetNamesOfControls();
if (elem[0] == resultID)
{
Mouse.Click(item);
}
}
}
The first three rows are OK. When the 4th step When Result 123 is selected is executed again var allResults = uIResult.FindMatchingControls(); is empty so the foreach part is skipped, no action is taken and the test is passed.
Could someone tell me what is wrong? It is obvious that I miss something.
Assuming you code is generated by recording (or is similar) then I would suspect the "cacheing" nature of the higher level UI Controls. The application may draw one UI Control containing the first 123 and draw another UI Control for the second 123. These controls look identical but are different and have different window-ids (or handles or whatever). One of the UI Controls in
WpfListItem uIResult = this.UIAUT.UITier1List.UITier2ListBox.UITier3ListItem;
probably refers to the UI Control of the first 123 even though it is no longer on display. I suspect UITier3ListItem or its child controls; if it were ...1... or ...2... then I would expect a failure message rather than zero matches.
Related
The saga of trying to chop flat files up into useable bits continues!
You may see from my other questions that I am trying to wrangle some flat file data into various bits using C# transformer in SSIS. The current challenge is trying to turn a selection of rows with one column into one row with many columns.
A friend has very kindly tipped me off to use List and then to somehow loop through that in the PostExecute().
The main problem is that I do not know how to loop through and create a row to add to the Output Buffer programatically - there might be a variable number of fields listed in the flat file, there is no consistency. For now, I have allowed for 100 outputs and called these pos1, pos2, etc.
What I would really like to do is count everything in my list, and loop through that many times, incrementing the numbers accordingly - i.e. fieldlist[0] goes to OutputBuffer.pos1, fieldlist[1] goes to OutputBuffer.pos2, and if there is nothing after this then nothing is put in pos3 to pos100.
The secondary problem is that I can't even test that my list and writing to an output table is working by specifically using OutputBuffer in PostExecute, never mind working out a loop.
The file has all sorts in it, but the list of fields is handily contained between START-OF-FIELDS and END-OF-FIELDS, so I have used the same logic as before to only process the rows in the middle of those.
bool passedSOF;
bool passedEOF;
List<string> fieldlist = new List<string>();
public override void PostExecute()
{
base.PostExecute();
OutputBuffer.AddRow();
OutputBuffer.field1=fieldlist[0];
OutputBuffer.field2=fieldlist[1];
}
public override void Input_ProcessInputRow(InputBuffer Row)
{
if (Row.RawData.Contains("END-OF-FIELDS"))
{
passedEOF = true;
OutputBuffer.SetEndOfRowset();
}
if (passedSOF && !passedEOF)
{
fieldlist.Add(Row.RawData);
}
if(Row.RawData.Contains("START-OF-FIELDS"))
{
passedSOF = true;
}
}
I have nothing underlined in red, but when I try to run this I get an error message about PostExecute() and "object reference not set to an instance of an object", which I thought meant something contained a null where it shouldn't, but in my test file I have more than two fields between START and END markers.
So first of all, what am I doing wrong in the example above, and secondly, how do I do this in a proper loop? There are only 100 possible outputs right now, but this could increase over time.
"Post execute" It's named that for a reason.
The execution of your data flow has ended and this method is for cleanup or anything that needs to happen after execution - like modification of SSIS variables. The buffers have gone away, there's no way to do interact with the contents of the buffers at this point.
As for the rest of your problem statement... it needs focus
So once again I have misunderstood a basic concept - PostExecute cannot be used to write out in the way I was trying. As people have pointed out, there is no way to do anything with the buffer contents here.
I cannot take credit for this answer, as again someone smarter than me came to the rescue, but I have got permission from them to post the code in case it is useful to anyone. I hope I have explained this OK, as I only just understand it myself and am very much learning as I go along.
First of all, make sure to have the following in your namespace:
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
These are going to be used to get properties for the Output Buffer and to allow me to output the first item in the list to pos_1, the second to pos_2, etc.
As usual I have two boolean variables to determine if I have passed the row which indicates the rows of data I want have started or ended, and I have my List.
bool passedSOF;
bool passedEOF;
List<string> fieldlist = new List<string>();
Here is where it is different - as I have something which indicates I am done processing my rows, which is the row containing END-OF-FIELDS, when I hit that point, I should be writing out my collected List to my output buffer. The aim is to take all of the multiple rows containing field names, and turn that into a single row with multiple columns, with the field names populated across those columns in the row order they appeared.
if (Row.RawData.Contains("END-OF-FIELDS"))
{
passedEOF = true;
//IF WE HAVE GOT TO THIS POINT, WE HAVE ALL THE DATA IN OUR LIST NOW
OutputBuffer.AddRow();
var fields = typeof(OutputBuffer).GetProperties();
//SET UP AND INITIALISE A VARIABLE TO HOLD THE ROW NUMBER COUNT
int rowNumber = 0;
foreach (var fieldName in fieldList)
{
//ADD ONE TO THE CURRENT VALUE OF rowNumber
rowNumber++;
//MATCH THE ROW NUMBER TO THE OUTPUT FIELD NAME
PropertyInfo field = fields.FirstOrDefault(x = > x.Name == string.Format("pos{0}", rowNumber));
if (field != null)
{
field.SetValue(OutputBuffer, fieldName);
}
}
OutputBuffer.SetEndOfRowset();
}
if (passedSOF && !passedEOF)
{
this.fieldList.Add(Row.RawData);
}
if (Row.RawData.Contains("START-OF-FIELDS"))
{
passedSOF = true;
}
So instead of having something like this:
START-OF-FIELDS
FRUIT
DAIRY
STARCHES
END-OF-FIELDS
I have the output:
pos_1 | pos_2 | pos_3
FRUIT | DAIRY | STARCHES
So I can build a position key table to show which field will appear in which order in the current monthly file, and now I am looking forward into getting myself into more trouble splitting the actual data rows out into another table :)
I have a table below that contains the students' results in a DataGridView:-
ID NAME RESULT
1 Peter PASS
1 Peter SILVER
2 Sam FAIL
2 Sam SILVER
3 Simon FAIL
4 Cliff PASS
5 Jason FAIL
5 Jason FAIL
6 Leonard PASS
6 Leonard FAIL
I'm trying to produce a simple program that will filter out certain rows base on the Results upon a click of a button. What I have achieved right now is that I have able to filter out those who with PASS and/or SILVER as their Result and only display out FAIL.
The problem is right now whenever the button is clicked, it will removed the rows with a PASS and/or SILVER, except the 2nd Row: 1 Peter SILVER. Leaving me with this table below as the end result:-
The only way to resolved this right now is to click the button again.
Below is the source code for the button:-
private void btnGenerate_Click(object sender, EventArgs e)
{
if (dtList.Rows.Count != 0)
{
try
{
foreach (DataGridViewRow dr in dtList.Rows)
{
//Column names in excel file
string colID = dr.Cells["ID"].Value.ToString();
string colName = dr.Cells["Name"].Value.ToString();
string colResult = dr.Cells["Result"].Value.ToString();
if (!colResult.Equals("FAIL", StringComparison.InvariantCultureIgnoreCase))
{
dtList.Rows.Remove(dr);
}
}
}
catch
{
}
}
}
The problem is you are changing the list you are iterating over. This is never a great idea...
In this case you look at the first row (Peter/Pass) and remove it. You then look at the second row. But wait, we removed a row so the second row is in fact actually now the old third row - we have just skipped the original second row.
You don't notice this problem anywhere else because all other rows that want to be removed are followed by rows you want to keep.
The way to fix this is to either:
Create a new list with the items you want to keep and then bind
that new list to whereever you are displaying
Create a list of items that you want to remove from the datatable
while you are iterating the table. Then once you have a list of
items you want to remove iterate over that list removing them from
the datatable.
Iterate through the list with a for loop starting with the last
index. This will mean that when you remove items you only effect
those that come after which in this case you will have already
processed.
The second is probably the easiest way to go in this situation. The first involves extra code and the third may not be obvious why you are doing that to somebody that comes after you.
I am trying to call a method using if..else. Below is the C# code from which i am trying to send a string country to get selected from dropdown. I expect method WePay() to get called. But getting the element not visible exception.
String country = "United States - US Dollars ($)";
new SelectElement(wd.FindElement(By.Id("CountrySelector"))).SelectByText(country);
if (country != "United States - US Dollars ($)")
{
PayPal();
}
else
{
WePay();
}
Below is the C# code for method WePay()
public void WePay()
{
wd.FindElement(By.XPath("//button[#onclick='ValidateSubmit()']")).Click();
wd.FindElement(By.Id("addAmount")).Click();
wd.FindElement(By.Id("addAmount")).SendKeys("10");
wd.FindElement(By.XPath("//button[#onclick='PayNow();']")).Click();
wd.FindElement(By.Id("new-cc")).Click();
new SelectElement(wd.FindElement(By.Id("Card_TransactionTypeId"))).SelectByText("Visa");
wd.FindElement(By.Id("Number")).SendKeys("4003830171874018");
wd.FindElement(By.Id("CVV")).SendKeys("527");
new SelectElement(wd.FindElement(By.Id("Card_ExpirationMonth"))).SelectByText("10");
new SelectElement(wd.FindElement(By.Id("Card_ExpirationYear"))).SelectByText("2020");
wd.FindElement(By.Id("saved-address")).Click();
new SelectElement(wd.FindElement(By.Id("AddressSelector"))).SelectByValue("69918");
wd.FindElement(By.Id("submitButton")).Click();
}
We can help you better, if you let us know what error message you are getting, and where exactly in the code you are getting the error.
I am just reframing your code below in Java:
String country = "United States - US Dollars ($)";
Select select=new Select(wd.FindElement(By.Id("CountrySelector"))
select.selectByVisibleText(country); //Here selects the country
//Now to cross check, get the value which is selected by below statement.
String returnSelectedCountry=select.getFirstSelectedOption(); //This will actually fetch the value from the listbox
if(!country.equals(returnSelectedCountry)) //Comparing the string var with actual val selected
PayPal();
else
WePay();
In my experience I had two solutions for this:
Use Wait, when interacting with elements especially when navigating through different pages. Most of the times JS is trying to interact with the HTML element before it has been loaded. In this case I extended the Selenium Wait with my own method that for given time checked/waited for the element condition.
When using Chrome/IE drivers keep in mind that some elements even load and present can't be handled properly. In my case Finish and Go Back buttons were at the bottom of the visible part of the screen and I had to use scroll-down option in order to interact with them.
Hope it helps.
I have a recording in Visual Studio 2012 for an application I'm testing. I record adding a person with a case (a child record). The case number is auto generated, so every time I run this test it's different.
When I playback my recording, it fails while trying to click on the link to the case record. This is because the link is different each execution.
The code that specifies this is in the designer (even after I "move" it to the UIMap file). Plus, I can't change it each time to predict what the number will be anyway.
How do I get the recording to select based on first link in the list (it's a gridview) rather than the specific link test and url?
A clever co-worker helped me out. Here is the solution he found:
replaced Mouse.Click(uIHHP2013000005Hyperlink, new Point(49, 2));
where had been defined in my UIMap designer with hard-coded values.
with:
HtmlControl control = new HtmlControl(this.UIPersonsCCDWindowsIntWindow.UIDetailsCCDDocument);
control.SearchProperties.Add(HtmlControl.PropertyNames.ClassName, "HtmlHyperlink");
UITestControlCollection controlcollection = control.FindMatchingControls();
List<string> names = new List<string>();
foreach (UITestControl x in controlcollection)
{
if (x is HtmlHyperlink)
{
HtmlHyperlink programCaseLink = (HtmlHyperlink)x;
if (programCaseLink.Href.StartsWith("http://mywebsite.com/ProgramCase/Details/"))
{
Mouse.Click(programCaseLink);
}
}
}
This should be a simple logic problem but for some reason I've been struggling with it for hours trying to come up with a semi-clean algorithm to implement this. I'm using MVC3 with a SQL Server background, but even if you don't know about MVC you might still be able to help me with the algorithm.
I'm writing an application that makes use of a wizard-like interface. For now, the navigation between those wizard screens is very linear (the next button goes to the page immediately after, previous button goes to page immediately before). Due to scope change (fun, I know) I've now been told to make this less linear.
For the first run-through, the user has to visit all pages in linear order, like so:
Step 1
Step 2
Step 3
SubStep 1
Sub-SubStep 1
Sub-SubStep 2
SubStep 2
Sub-SubStep 1
Sub-SubStep 2
...
SubStep *n*
Sub-SubStep 1
Sub-SubStep 2
Submission
Where n is variable based on something that was entered in Step 2.
After the wizard is submitted it is reviewed by an administrator. If they find information missing they can unlock certain pages. When the user goes back to enter that information they should only be able to view those certain pages. For example, navigation might be like so:
Step 2
Step 3
SubStep 1
Sub-SubStep2
Submission
My current implementation consists of a table in the database that keeps track of the unlocked pages. When the "Next" button is clicked it calls a method to determine what the next page is. Because of the strange and variable navigation that takes place in Step 3 this method is an if-else branching nightmare which is easily broken.
Any suggestions on simplifying this would be greatly appreciated.
If you create a tree structure that represents the navigation hierarchy, a preorder traversal of the tree will hit the pages in the desired linear order. You can run such a traversal, and when you hit the current page, you can continue the traversal until you find an unlocked page, which will be the desired next page.
Pseudocode:
class TreeNode:
string name
List<TreeNode> children
string findNextPage(TreeNode node, Set<string> unlockedPageNames,
string currentPageName, ref bool currentPageFound):
if currentPageFound && unlockedPageNames.Contains(node.name):
return node.name
if node.name == currentPageName:
currentPageFound = true
foreach child in children:
result = findNextPage(child, unlockedPageNames,
currentPageName, currentPageFound)
if result != null:
return result
return null
string findNextPage(TreeNode node, Set<string> unlockedPageNames,
string currentPageName):
bool currentPageFound = false
return findNextPage(node, unlockedPageNames,
currentPageName, currentPageFound)
Note that you need a root node, whose children must be Step 1, Step 2, and Step 3. Pass this root node to the last findNextPage() function.