Accessing a Syncfusion Dropdown through Selenium Unit Tests - c#

In my unit test, I'm trying to assert that a user can access and select an item from a dropdown menu. I have built every .FindElement() extension I can do, but when running the Unit Test it isn't finding the dropdown menu. Could anybody help me figure out how to select it? The list of dropdown items comes from a list built elsewhere in the code base.
# Syncfusion Dropdown List Code
<SfDropDownList TItem="Industry" TValue="string" Placeholder="Select..." PopupHeight="20px" DataSource="#Industries" AllowFiltering="true" #bind-Value="Industry" ID="industry">
<DropDownListEvents TItem="Industry" TValue="string" ValueChange="#(ChangeIndustry)"></DropDownListEvents>
<DropDownListFieldSettings Text="IndustryName"></DropDownListFieldSettings>
</SfDropDownList>
#Test
var industry = driver.FindElement(By.Id("industry"));
var selectElement = new SelectElement(industry);
selectElement.SelectByText("Construction");
//Assert
Assert.Contains("Construction", industry.Text);
#HTML
<div class="mb-2 position-relative">
<label class="label-client" for="Industry">Industry</label>
-- <Dropdown Code Above> --
</div>

So the XPath I was using was wrong, even though I'd followed it through with the generated HTML, I installed the Selenium Edge Extension and used this to record the test I was doing. There is an option in there to change the outputs from CSS select to XPath, which gave me the correct path I was looking for.
I think it must be something to do with the way Syncfusion components generate once the browser is loaded up, but the class and IDs were completely different to the ones I had thought they were. Thanks for the help #JeffC!

The SelectElement class is only for the SELECT HTML element and can't be used here. You'll have to click the element to open the dropdown and then find the desired element based on text contained using an XPath. You haven't provided the HTML generated in the browser for the dropdown so I can't provide a specific locator but this should give you the general idea.
var industry = driver.FindElement(By.Id("industry"));
industry.click(); // open the dropdown
driver.FindElement(By.XPath("//*[.='Sample option']")).click();

You can also use the below code to select the value in the dropdown.
[Fact(DisplayName = "Select item when click the list item")]
public async Task SelectItem()
{
var data = GetDataItems();
var dropdown = RenderComponent<SfDropDownList<string, Countries>>(parameters =>
parameters.Add(p => p.DataSource, data).AddChildContent<DropDownListFieldSettings>(field => field.Add(p => p.Text, "Name").Add(p => p.Value, "Code")));
await dropdown.Instance.ShowPopup();
var popupEle = dropdown.Find(".e-popup");
var liColl = popupEle.QuerySelectorAll("li.e-list-item");
liColl[3].Click();
Assert.Contains("e-active", liColl[3].ClassName);
Assert.Contains("Cameroon", dropdownlist.Instance.Text);
var focusItem = popupEle.QuerySelector("li.e-item-focus");
Assert.Null(focusItem);
}
private List<Countries> GetDataItems()
{
return new List<Countries>
{
new Countries() { Name = "Australia", Code = "AU" },
new Countries() { Name = "Bermuda", Code = "BM" },
new Countries() { Name = "Canada", Code = "CA" },
new Countries() { Name = "Cameroon", Code = "CM" },
new Countries() { Name = "Denmark", Code = "DK" },
new Countries() { Name = "France", Code = "FR" }
};
}

Related

botbuilder v 4, dynamic adaptive card with dropdown and capturing values on prompt

I'm using ms botbuilder v 4
I'm using webcontrol, webchat.js, latest, react
Case is pretty trivial:
I want to show list of possible values in dropdown, values will be dynamic (comes from API, i need Titles and Values (Ids) there. Then when user selects some item and clicks OK i want to get value (Id) and work further with that.
As i got it for now only way to show dropdown is using adaptive cards, in v3 there was an option to use adaptive cards in prompts and it also planned for next version: https://github.com/Microsoft/botbuilder-dotnet/issues/1170
But for now only woraround for that is exaplained here:
https://github.com/Microsoft/botbuilder-dotnet/issues/614 , with just list of string everything's working fine, but if i want to store keyvalue pairs (for IDs) i'm not able to do that cos Choices in PromptOptions only accepts list of string (will show below). So only workaround i'm using now is to store whole collection of values and after getting the result go and find it's id. Is there more convinient solution for that?
Here's the code:
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = s.Value}).ToList();
var card = new AdaptiveCard
{
Version = new AdaptiveSchemaVersion(1, 0),
Body =
{
new AdaptiveTextBlock("Select a team to assign your ticket"),
new AdaptiveChoiceSetInput
{
Choices = choicesInputs,
Id = "setId",
Style = AdaptiveChoiceInputStyle.Compact,
IsMultiSelect = false
}
},
Actions = new List<AdaptiveAction>
{
new AdaptiveSubmitAction
{
Title = "Ok",
Type = "Action.Submit"
}
}
};
signInPhoneState.Teams = _teams;
return await stepcontext.PromptAsync(
"SelectGroupCardDialog",
new PromptOptions
{
Choices = ChoiceFactory.ToChoices(_teams.Select(pair => pair.Value).ToList()),
Prompt = (Activity) MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card))
})
},
cancellationtoken);
// . . .
var selectedTeamId = signInPhoneState.Teams.FirstOrDefault(pair => pair.Value == sel).Key;
Quick side question (but related in terms i'm using it for workaround):
What is the easiest way to persist some variable though dialog? If i remember correectly In v3 it was as simple as marking a value as public and marking dialog as serializable and that's it, now as i get it you need to create special accessor for each dialog, dublicate property there and manage the state of it, is it correct?
Thanks
You have a dictionary with team ID's as keys and team names as values. You are using the team names as the values for an adaptive choice set that's being used in a prompt, and in the turn after the prompt you're extracting the team ID from the dictionary using the team name. You want a more convenient option.
Option 1: If you're okay with your current setup of keeping the dictionary available
When accessing the data in a dictionary, it is more efficient to access a value using a key than the other way around. That is what dictionaries are for, after all. So instead of using the team names as values in your choice set, you could use team ID's.
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = s.Key }).ToList();
// . . .
signInPhoneState.Teams.TryGetValue(sel, out string selectedTeamName);
This would mean that if the dictionary is being drawn from some external source that's subject to change, the team name would be as up-to-date as possible.
Option 2: If you don't want to depend on the dictionary for the next turn
You could store both the team ID and the team name in the choice's value.
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = JsonConvert.SerializeObject(s) }).ToList();
// . . .
var pair = JsonConvert.DeserializeObject<KeyValuePair<string, string>>(sel);
var selectedTeamId = pair.Key;
var selectedTeamName = pair.Value;
This would mean if the underlying data changes between the first turn of the prompt and the second, the choice would still be valid.

Binding the dropdownlist to the mvc view

I am trying to bind the dropdown list to the data-set coming from the data context class in mvc 6. I wrote a function to get the populated list but unable to reproduce the same using razor. Here's what I have so far. Please note that I have not created a model yet. trying to make use of the generated POCO class from the database scaffolding.
function on Layout.cshtml
#functions{
public List<HSIP.Entities.StateDetails> function1()
{
// protected readonly HSIP.Entities.HSIPContext context;
HSIP.Entities.HSIPContext hsipcontext = new HSIP.Entities.HSIPContext();
List<HSIP.Entities.StateDetails> getstatelist = (from s in hsipcontext.StateDetails
select new HSIP.Entities.StateDetails
{
StateDesc = s.StateDesc,
StateCode = s.StateCode,
StateAbbr = s.StateAbbr
}).ToList();
//SelectList list = new SelectList(getstatelist, "Region", "StateCode", "StateAbbr", "StateDesc");
return getstatelist;
}
}
Razor syntax:
#Html.DropDownList("StateDesc", #function1(), "Please select State Name");
The Razor syntax throws an error: there is no argument given that corresponds to the required formal parameter 'htmlattributes' of IHTMLHelper.Dropdownlist(string, IEnumerable, string, object).
can someone please point me in the right direction.
Thanks,
Hari
I am prefer do this:
In a controller/Model:
using System.Web.Mvc;
public List<SelectListItem> DropdownListFilter()
{
var listitem = new List<SelectListItem>();
listitem.Add(new SelectListItem { Text = "Dropdown1", Value = "0", Selected = true });
listitem.Add(new SelectListItem { Text = "Dropdown2", Value = "1", Selected = false });
listitem.Add(new SelectListItem { Text = "Dropdown3", Value = "2", Selected = false });
return listitem;
}
When I Load in the ActionResult Just add this following Line:
ViewBag.FilterDropdown = ar.DropdownListFilter().ToList();
And in the view you have to call Filter dropdown like this:
#Html.DropDownList("FilterDropdown")
Hope this help.
Firstly use a SelectListItem in your controller and pass it to your view.Then use it in Razor syntax to populate the dropdown.
List<SelectListItem> stateList = (from s in hsipcontext.StateDetails
select new HSIP.Entities.StateDetails
{
StateDesc = s.StateDesc,
StateCode = s.StateCode,
StateAbbr = s.StateAbbr
}).ToList();
View:
#Html.DropDownListFor("StateDesc", stateList ,"Please select State Name")

Seeding data in Configuration.cs, creating duplicate data with AddOrUpdate

I am using Migrations in an MVC 4 EF5 application in Visual Studio 2012 Express with SQL Server 2012 Express, using Code First.
I use the Seed method in configuration.cs, firstly creating a Tags table. When I execute 'Update-Database -verbose -force' from Package Manager, it works correctly and doesn't create duplicate tags - and re-creates them if deleted:
db.Tags.AddOrUpdate(
t => t.Name,
new Tag { Name = "Bakery", NamePlural = "Bakeries" },
new Tag { Name = "Bar", NamePlural = "Bars" },
new Tag { Name = "Bookshop", NamePlural = "Bookshops" }
);
db.SaveChanges();
I then try and add related Places data:
db.Places.AddOrUpdate(
p => p.Name,
new Place
{
Name = "Shoreditch Grind",
URL = "shoreditch-grind-cafe",
Address = "213 Old St",
City = "London",
PostCode = "EC1V 9NR",
Website = "www.shoreditchgrind.com",
Phone = "020 7490 0101",
About = "Good coffee on the Silicon Roundabout",
Image = "noimage.png",
Tag = db.Tags.Single(t => t.Name == "Bar")
},
new Place
{
Name = "The Old Blue Last",
URL = "old-blue-last-pub",
Address = "38 Great Eastern St",
City = "London",
PostCode = "EC2A 3ES",
Website = "www.theoldbluelast.com",
Phone = "020 7739 7033",
About = "Pub of Vice Magazine",
Image = "noimage.png",
Tag = db.Tags.Single(t => t.Name == "Bakery")
}
);
This however creates duplicates, adding all the places again every time I execute 'Update-Database -verbose -force'
I'm new to MVC - and I also don't fully understand what this does:
p => p.Name,
I have a feeling perhaps I should be manually adding ID values to each object?
How can I run this without creating duplicate Places?
It would also be useful to be able to mark each Tag.Name as unique simply.
Thanks.
This may work:
var place = new Place
{
Name = "The Old Blue Last",
URL = "old-blue-last-pub",
Address = "38 Great Eastern St",
City = "London",
PostCode = "EC2A 3ES",
Website = "www.theoldbluelast.com",
Phone = "123 456 789", // updated number
About = "Pub of Vice Magazine",
Image = "noimage.png",
TagID = db.Tags.Single(t => t.Name == "Bakery").TagID
};
db.Places.AddOrUpdate(p => p.Name, place);
db.SaveChanges();
Since "The Old Blue Last" is already there, and we've updated based on p.Name, it should only update that entry changing Phone to "123 456 789". This similar to what you have tried, but may work. See more here.
You also mentioned that you are not sure what p => p.Name does. The => is called a Lambda Expression. It is an anonymous function. It is a method without a declaration, access modifier, return type, name etc. It's a short hand expression that allows you to write a method in the place you are going to use it.
See more here and here..

Errors when creating a custom Querable object with MVC and Subsonic pagedlist

hiya, i have the following code but when i try and create a new IQuerable i get an error that the interface cannot be implemented, if i take away the new i get a not implemented exception, have had to jump back and work on some old ASP classic sites for past month and for the life of me i can not wake my brain up into C# mode.
Could you please have a look at below and give me some clues on where i'm going wrong:
The code is to create a list of priceItems, but instead of a categoryID (int) i am going to be showing the name as string.
public ActionResult ViewPriceItems(int? page)
{
var crm = 0;
page = GetPage(page);
// try and create items2
IQueryable<ViewPriceItemsModel> items2 = new IQueryable<ViewPriceItemsModel>();
// the data to be paged,but unmodified
var olditems = PriceItem.All().OrderBy(x => x.PriceItemID);
foreach (var item in olditems)
{
// set category as the name not the ID for easier reading
items2.Concat(new [] {new ViewPriceItemsModel {ID = item.PriceItemID,
Name = item.PriceItem_Name,
Category = PriceCategory.SingleOrDefault(
x => x.PriceCategoryID == item.PriceItem_PriceCategory_ID).PriceCategory_Name,
Display = item.PriceItems_DisplayMethod}});
}
crm = olditems.Count() / MaxResultsPerPage;
ViewData["numtpages"] = crm;
ViewData["curtpage"] = page + 1;
// return a paged result set
return View(new PagedList<ViewPriceItemsModel>(items2, page ?? 0, MaxResultsPerPage));
}
many thanks
you do not need to create items2. remove the line with comment try and create items2. Use the following code. I have not tested this. But I hope this works.
var items2 = (from item in olditems
select new ViewPriceItemsModel
{
ID = item.PriceItemID,
Name = item.PriceItem_Name,
Category = PriceCategory.SingleOrDefault(
x => x.PriceCategoryID == item.PriceItem_PriceCategory_ID).PriceCategory_Name,
Display = item.PriceItems_DisplayMethod
}).AsQueryable();

How can I make Html.DropDownList encode its values?

In my viewData I have an IList mls.
I want to use this to show in a dropdown. Like so:
<%= Html.DropDownList("ml3Code",
new SelectList(Model.Mls, "Code", "Description", Model.Ml3.Code ?? ""),
Model.T9n.TranslateById("Labels.All"),
new { #class = "searchInput" })%>
This works fine, until there's a myObject.Code == VOC<420 g/l.
I would have expected that an HTML helper would encode its values, but it doesn't.
How should I approach this problem? The only thing I can come up with is first making a dupe list of the objects with encoded values and then feeding it to the selectlist. This would be really bothersome.
P.S. I hope Phill H. and his team will have a long and thorough look at the encoding for asp.net-mvc 2.0...
I'm puzzled. The question "Do ASP.NET MVC helper methods like Html.DropDownList() encode the output HTML?" was asked on SO before, and the answer was "Yes" - and the source-code from the MVC framework was cited to back this assertion up.
Well, you can roll your own Html helper, but if you're like me you won't want to do that.
To me, I see two options here:
Write your select element in plain view without the helper. I've never felt the helpers provide you much save for highlighting an element when an error occurs.
Patch the select box on the client when the page loads, as in:
function encodeHtml(str)
{
var encodedHtml = escape(str);
encodedHtml = encodedHtml.replace(///g,"%2F");
encodedHtml = encodedHtml.replace(/\?/g,"%3F");
encodedHtml = encodedHtml.replace(/=/g,"%3D");
encodedHtml = encodedHtml.replace(/&/g,"%26");
encodedHtml = encodedHtml.replace(/#/g,"%40");
return encodedHtml;
}
window.onload = function()
{
var ml3Code = document.getElementById("ml3Code");
for(var i = 0; i < ml3Code.options.length; ++i)
{
ml3Code.options[i].value = encodeHtml(ml3Code.options[i].value);
}
};
It's a hack, I know. I strongly prefer the first choice.
This is encoded. But dont check with firebug - It shows values decoded.
Check in ViewSource of the Browser and things are encoded.
Controller
public List<CategoryInfo> GetCategoryList()
{
List<CategoryInfo> categories = new List<CategoryInfo>();
categories.Add(new CategoryInfo { Name = "Food<äü", Key = "VOC<420 g/l", ID = 2, Uid = new Guid("C0FD4706-4D06-4A0F-BC69-1FD0FA743B07") });
}
public ActionResult Category(ProductViewModel model )
{
IEnumerable<SelectListItem> categoryList =
from category in GetCategoryList()
select new SelectListItem
{
Text = category.Name,
Value = category.Key
};
model.CategoryList = categoryList;
return View(model);
}
View
<%= Html.DropDownList("Category" , Model.CategoryList) %>
Model
public class ProductViewModel
{
public IEnumerable<SelectListItem> CategoryList { get; set; }
public List<CategoryInfo> Categories { get; set; }
}
HTML
<select id="Category" name="Category"><option value="VOC<420 g/l">Food<äü</option>
</select>

Categories