How to open a blazored modal with bunit? - modal-dialog

I want to test whether a modal opens up or not with bunit. The problem is, that the modal doesn't get rendered. How to open a blazored modal with bunit?
Modal Creation in my component under test:
<div style="display: flex; justify-content: flex-end">
<button class="btn btn-success
btn-lg"
id="openModalButton"
#onclick="CheckOpenModal">
Hinzufügen
</button>
</div>
#code
{
[CascadingParameter] public IModalService Modal { get; set; }
private async Task OpenModalForCreation()
{
List<string> ParameterA = new List<string>();
var parameters = new ModalParameters();
parameters.Add(nameof(CreationModal.ParameterA), ParameterA);
Modal.Show<CreationModal>("Create something", parameters);
}
}
My TestClass:
public class PrivateMachinesCompTest : TestContext
{
public CompTest()
{
Services.AddBlazoredModal();
}
[Fact]
public void CheckOpenModal()
{
modalService = new ModalService();
var cut = RenderComponent<ComponentUnderTest>(parameters => parameters
.AddCascadingValue(modalService));
var openModalButton = cut.Find("#openModalButton");
openModalButton.Click();
cut.MarkupMatches("Create something");
}

The problem is that you are not rendering the component that actually does the rendering. Just passing in an IModalService doesn't do it.
My approach would be to create a mock of IModalService and assert that the expected method on it is called.

Related

bind- value to doesn't work in radio button in blazor

For the following code, for some reason the selection of the radio buttons binds x.MyCapability with -> "on" instead of the label of the button (let's assume the labels of the radio element are *a ,*b and *c):
#foreach (var capability in myList)
{
<label>
<input type="radio" name="MyCapability" SelectedValue="MyCapability" #bind-value="x.MyCapability" />
#capability <text> </text>
</label>
}
how can I link x.MyCapability with a, b or c?
.NetCore 3.1
You are binding in a wrong way, here is the way you can
Your html
<div>Your Capability: #MyCapability</div>
<div>
#foreach (var capability in myList)
{
<div>
<label>#capability.MyCapability</label>
<input type="radio"
name="#capability.MyCapability"
#onchange=#(() => MyCapability = capability.MyCapability)
checked="#(capability.MyCapability==MyCapability)">
</div>
}
</div>
Here is the code behind
#code{
private string MyCapability = "a";
private List<Capabilities> myList = new()
{
new Capabilities()
{
MyCapability = "a"
},
new Capabilities()
{
MyCapability = "b"
},
new Capabilities()
{
MyCapability = "c"
}
};
public class Capabilities
{
public string MyCapability { get; set; }
}
}
When you select any option it will be update in above div

From an select item in the list, create another listbox ZK

I had a headache with this. I want to choose a book from the 1st list and with that book create a second list to be able to show the details of the book (title, number of pages)
Here is the code:
public class Book {
private int numBook;
private String nameBook;
private String author;
public Book(int numBook, String nameBook, String author) {
super();
this.numBook = numBook;
this.nameBook = nameBook;
this.author = author;
}
public int getNumBook() {
return numBook;
}
public void setNumBook(int numBook) {
this.numBook = numBook;
}
public String getNameBook() {
return nameBook;
}
public void setNameBook(String nameBook) {
this.nameBook = nameBook;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
Class BookData: Load the info in array
public class BookData {
private List<Book> books = new ArrayList<Book>();
public BookData() {
loadBooks();
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
public void loadBooks() {
Book b;
for(int i = 0; i<4;i++){
b = new Book(i+1, "Libro "+i+1, "Author "+i+1);
books.add(b);
}
}
}
Class BookViewModel: ViewModel of Listbox
public class BookViewModel {
private static Book selectedBook;
private List<Book> booksData = new ArrayList<Book>(new BookData().getBooks()); // Armo los libros
public List<Book> getBooksData() {
return booksData;
}
public void setBooksData(List<Book> booksData) {
this.booksData = booksData;
}
//Getters and Setter the SelectedCar
#NotifyChange("selectedBook")
public Book getSelectedBook() {
if(selectedBook!=null) {
//setSelectedBook(selectedBook);
new DetailData(selectedBook);
//new ArrayList<>(new DetailData().getDetailsFilterByBook());
//Then here pass the Book Selected
}
return selectedBook;
}
public void setSelectedBook(Book selectedBook) {
this.selectedBook = selectedBook;
}
}
Class Detail: Detail Model of the choose Book
public class Detail {
private int idBook;
private String title;
private int numPages;
public Detail(int idBook, String title, int numPages) {
this.idBook = idBook;
this.title = title;
this.numPages = numPages;
}
public int getIdBook() {
return idBook;
}
public void setIdBook(int idBook) {
this.idBook = idBook;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getNumPages() {
return numPages;
}
public void setNumPages(int numPages) {
this.numPages = numPages;
}
#Override
public String toString() {
return "Detail [idBook=" + idBook + ", title=" + title + ", numPages=" + numPages + "]";
}
}
Class DetailData: Load the data in array
//Clase que se ecarga de manejar la data
public class DetailData {
private List<Detail> details = loadAllDetails();
private List<Detail> detailsFilterByBook;
private static Book bookSelected;
/*public DetailData(){
//Previously all the data is loaded
System.out.println(bookSelected);
detailsFilterByBook = new ArrayList<>();
filterDetailsByBook();
}*/
public void setBookSelected(Book bookSelected){
this.bookSelected = bookSelected;
}
public DetailData(){
this(bookSelected);
}
public DetailData(Book b){
bookSelected = b;
System.out.println(bookSelected);
detailsFilterByBook = new ArrayList<>();
filterDetailsByBook();
}
public List<Detail> loadAllDetails(){
List tmp = new ArrayList<Detail>();
//Libro 1
Detail d1b1 = new Detail(1, "Preview", 15);
Detail d2b1 = new Detail(1, "Inicio", 10);
Detail d3b1 = new Detail(1, "Zk Bind", 50);
//Libro 2
Detail d1b2 = new Detail(2, "Introduccion", 15);
Detail d2b2 = new Detail(2, "JAVA", 100);
Detail d3b2 = new Detail(2, "CSS", 25);
//Libro 3
Detail d1b3 = new Detail(3, "HTML", 35);
Detail d2b3 = new Detail(3, "Javascript", 40);
Detail d3b3 = new Detail(3, "Ajax", 25);
//Libro 4
Detail d1b4 = new Detail(4, "Android", 100);
Detail d2b4 = new Detail(4, "IOS", 100);
tmp.add(d1b1);
tmp.add(d2b1);
tmp.add(d3b1);
tmp.add(d1b2);
tmp.add(d2b2);
tmp.add(d3b2);
tmp.add(d1b3);
tmp.add(d2b3);
tmp.add(d3b3);
tmp.add(d1b4);
tmp.add(d2b4);
return tmp;
}
private void filterDetailsByBook() {
for(Detail d:details){
if(d.getIdBook() == bookSelected.getNumBook())
detailsFilterByBook.add(d);
}
print();
}
public void print(){
System.out.println("Imprimiendo detalles del libro escogido");
for(Detail d: detailsFilterByBook){
System.out.println(d);
}
}
public List<Detail> getDetails() {
return details;
}
public void setDetails(List<Detail> details) {
this.details = details;
}
public List<Detail> getDetailsFilterByBook() {
return detailsFilterByBook;
}
public void setDetailsFilterByBook(List<Detail> detailsFilterByBook) {
this.detailsFilterByBook = detailsFilterByBook;
}
}
Class: DetailViewModel:ViewModel of the second ListBox
public class DetailViewModel {
private List<Detail> detailsData = new ArrayList<>();
#NotifyChange("detailsData")
public void refreshList(){
System.out.println("REFRESH");
detailsData = new ArrayList<>(new DetailData().getDetailsFilterByBook());
}
public List<Detail> getDetailsData() {
return detailsData;
}
#NotifyChange("detailsData")
public void setDetailsData(List<Detail> detailsData) {
this.detailsData = detailsData;
}
}
Here is the zul file
<window title="" border="none" height="100%" apply="org.zkoss.bind.BindComposer" viewmodel="#id('vm') #init('book.BookViewModel')">
<listbox model="#bind(vm.booksData)" selecteditem="#bind(vm.selectedBook)" emptymessage="No car found in the result">
<listhead>
<listheader label="Num Libro"/>
<listheader label="Libro"/>
<listheader label="Autor"/>
</listhead>
<template name="model" var="book">
<listitem>
<listcell label="#bind(book.numBook)"/>
<listcell label="#bind(book.nameBook)"/>
<listcell label="#bind(book.author)"/>
</listitem>
</template>
</listbox>
<separator height="100px"/>
<window title="" border="none" height="100%" apply="org.zkoss.bind.BindComposer"
viewModel="#id('vm') #init('detail.DetailViewModel')">
<listbox model="#bind(vm.detailsData)" emptyMessage="No existen datos que presentar">
<listhead>
<listheader label="Num Capitulos"/>
<listheader label="Titulo del Cap"/>
</listhead>
<template name="model" var="detail">
<listitem>
<listcell label="#bind(detail.idBook)"/>
<listcell label="#bind(detail.title)"/>
<listcell label="#bind(detail.numPages)"/>
</listitem>
</template>
</listbox>
</window>
</window>
I try in the second listbox (At begin have to be empty), show the details of the book everytime when a book in the 1st listbox is selected. I get the correct info. When I choose a book, I get the correct details of that book, but my second listbox does'nt show anything. I will apreciate all the help. PD: Sorry for the english
Oke, there are more points to say on this code then you imagine.
Never use static for a user/session variable.
In your VM you have the following code :
private static Book selectedBook;
Imagine that I select Book 1 and you select 2 seconds later Book 2.
Because it's static, I'm also having Book 2 selected, while mine view isn't aware of it.
This means the GUI and server side are out of sync => never a good thing.
If you could be able to sync the view with the selected item, this means that you select book 2 for me and I'll be searching the number of the Ghost Busters.
With ZK, always use ListModel interface to give collections to GUI.
While returning List<Book> works pretty good, you need to understand the consequences of this action.
A List or Grid expect an implementation of ListModel and if you don't give it, there will be one created every time you notify the list of a change.
While this is a nice to have feature it also removes the intelligence of a listmodel and the GUI rendering will be a lot more.
An example is always more clear :
We have a Collection of 9 items and we will append 1 to it.
Adding 1 Object to the List and notifying it implies that all the content rendered of the Listbox will be removed and then adding all the content again to the Listbox.
This means that we are removing and adding 9 lines who aren't changed.
Adding 1 Object to a ListModel, even without notifying the ListModel of a change will result in an action where there is only 1 item appended to the Listbox. This is the intelligence of a ListModel => adding and removing items will be persisted to the GUI without overhead.
So your code should be looking like this :
private Book selectedBook;
private final ListModelList<Book> booksData = new ListModelList<Book>(new BookData().getBooks()); // Armo los libros
Why not working to the interface and why final?
So I just told you about the interface ListModel and yet, I'm setting an implementation of ListModel as code, even while we learn to work against interfaces.
The simple reason is that ListModel doesn't have methods for appending and removing items while the implementation do have it.
So I make a decision to work against that object in stead of casting it when I need the methods.
Remember, the global getter for the booksData can look like this :
public ListModel<Book> getBooksData() {
return booksData;
}
So here we hide the implementation of ListModelList to the outside.
The reason for final is that you will forcing yourself or other people who are going through the code to use the clear() method in stead of making a new ListModelList.
It's just not needed to create a new instance of it.
Using 2 viewmodel's
Your making yourself difficult of using 2 VM's.
But while it's sometimes a good idea to do this I'll be helping you to get your problem solved.
Your first problem is one of a naming kind.
Viewmodel 1 => called vm in the zul.
Viewmodel 2 => called vm in the zul.
You see it coming? who will listen when I cry to vm?
let's call the viewmodel of the details detailVM
viewModel="#id('detailVM') #init('detail.DetailViewModel')"
The second problem is that your detail viewmodel doesn't have any clue of the first listbox.
What do I want to say is that your second viewmodel should be holding the correct info of the selected item of the first listbox.
Zul code should be looking like this :
<window title="" border="none" height="100%" apply="org.zkoss.bind.BindComposer" viewmodel="#id('vm') #init('book.BookViewModel')">
<div apply="org.zkoss.bind.BindComposer"
viewModel="#id('detailVM') #init('detail.DetailViewModel')">
<listbox model="#init(vm.booksData)" selecteditem="#bind(detailVM.selectedBook)" emptymessage="No book found in the result">
<listhead>
<listheader label="Num Libro"/>
<listheader label="Libro"/>
<listheader label="Autor"/>
</listhead>
<template name="model" var="book">
<listitem>
<listcell label="#load(book.numBook)"/>
<listcell label="#load(book.nameBook)"/>
<listcell label="#load(book.author)"/>
</listitem>
</template>
</listbox>
<separator height="100px"/>
<listbox model="#init(detailVM.detailsData)" emptyMessage="No existen datos que presentar">
<listhead>
<listheader label="Num Capitulos"/>
<listheader label="Titulo del Cap"/>
</listhead>
<template name="model" var="detail">
<listitem>
<listcell label="#load(detail.idBook)"/>
<listcell label="#load(detail.title)"/>
<listcell label="#load(detail.numPages)"/>
</listitem>
</template>
</listbox>
</div>
</window>
So I set you up with the correct zul, and now it's up to you to modify the viewmodels.
Remember that I set selectedBook in detailVM so now it's not needed in the first viewmodel.
I don't write everything for you, otherwise you wouldn't learn from it.
Some small things left to say.
You see I change the listbox model to #init and not #bind.
A model is always read only, so please NEVER NEVER NEVER use #bind.
#load is the highest annotation you could use, and this is only the case when you will create a new instance for the ListModel, witch is hardly needed.
Labels, are also not updatable in your GUI.
Again #bind is over the top, #load should be used in normal situations (when the value can change, so most commonly) or #init when the value will never change, but if you use #load I'll be happy already.
Hope this could set you to the right direction.
If you have any other question, just comment below.

MVC 5 - Pass object to a shared view

I am developing a MVC 5 internet application and have a question in regards to passing an object to a shared view.
I have a view called CustomError.cshtml in the shared folder. This view has the following model type: #model CanFindLocation.ViewModels.CustomErrorViewModel
How can I pass an object of type CanFindLocation.ViewModels.CustomErrorViewModel to this view from the protected override void OnException(ExceptionContext filterContext) function in a controller?
Here is my code:
protected override void OnException(ExceptionContext filterContext)
{
Exception e = filterContext.Exception;
if (e is HttpRequestValidationException)
{
filterContext.ExceptionHandled = false;
customErrorViewModel = customErrorService.GetDefaultCustomError(customErrorType, "Test message.");
RedirectToAction("CustomError", customErrorViewModel);
}
}
Instead of the view being shown, the following function is called:
protected void Application_Error(object sender, EventArgs e)
Thanks in advance.
I don't think you can return view as you want, so I usually put values into TempData and make a redirection to homepage or whatever landing page.
Homepage check is there is value into this Viewbag and show error if there is error.
Controller:
public class BaseController : Controller
{
protected void SetError(string message, params object[] args)
{
TempData["UIError"] = string.Format(message, args);
}
}
In in my shared (master) layout view:
#if (TempData["UIError"] != null)
{
<div class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span>
#TempData["UIError"]
</div>
}

Wicket submit action doesn't work

After updating Wicket from version 6.12 to 6.13/6.14 onSubmit action doesn't work. For Example class:
public class LoginPage extends WebPage {
private String username = "";
private String password = "";
public LoginPage() {
super();
Form<?> form = new Form<Void>("form");
setDefaultModel(new CompoundPropertyModel<>(this));
form.add(new Button("submit") {
#Override
public void onSubmit() {
System.out.println("SUBMIT "+username+":"+password);
}
});
form.add(new TextField<String>("username").setRequired(true));
form.add(new PasswordTextField("password").setRequired(true));
add(form);
}
}
with HTML:
<!DOCTYPE html>
<html xmlns:wicket>
<body>
<form wicket:id="form">
<input id="name" type="text" placeholder="Username" wicket:id="username">
<input id="password" type="password" placeholder="Password" wicket:id="password">
<input type="submit" wicket:id="submit" value="Enter">
</form>
</body>
</html>
doesn't works with wicket version 6.13+ and great work with wicket 6.12-. Changing Button on something like SubmitLink doesn't help.
Could you tell me what's wrong?
Well... hacky but it seems to work with 6.15.
Replace encodePageComponentInfo with the following one.
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
Args.notNull(url, "url");
if (info != null) {
String s = info.toString();
if (!Strings.isEmpty(s)) {
try {
Integer.parseInt(s);
} catch (Exception e) {
QueryParameter parameter = new QueryParameter(s, "");
url.getQueryParameters().add(parameter);
}
}
}
}
I found problem in my test project. I use changed MountedMapper for hiding version number in the URL:
/**
* Wrapper for hiding the version number in the URL
*/
public class SimpleMountedMapper extends MountedMapper {
public SimpleMountedMapper(String mountPath, Class<? extends IRequestablePage> pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
}
public Url mapHandler(IRequestHandler requestHandler) {
if (requestHandler instanceof ListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
In new version of wicket something wrong with this implementation (got it from this question).

How would one use a [Wicket] ListView with a form?

I have added a Form on submit of which I have to add more wicket controls like Labels, textfields and button with an Ajex Link. But not able to get the correct HTML. Can anyone please help me to get rid of it ?
voucherPanel.html
<html xmlns:wicket>
<head>
</head>
<body>
<wicket:panel>
<div class="form-block">
<div wicket:id="form">
<wicket:message key="lbl.vouchercode" />
<div wicket:id="list">
<input wicket:id="word" type="text" />
</div>
<div wicket:id="vouchercode"></div>
<button wicket:id="submit"><wicket:message key="submitText"/></button>
</div>
</div>
</wicket:panel>
</body>
</html>
voucherPanel.java
public class VoucherPanel extends Panel
{
private static final long serialVersionUID = 1L;
public VoucherPanel(final String id)
{
super(id);
final TextField<String> voucherCodeField = new TextField<String>("vouchercode", Model.of(""));
voucherCodeField.setRequired(true);
final Button button = new Button("submit");
Form<?> form = new Form<Void>("form")
{
#Override
protected void onSubmit()
{
numberOfFields = new ArrayList<String>();
int noOfVocuhers = getNoOfAllowedVoucher();// just returing the number
for (int i = 0; i < noOfVocuhers; i++) {
numberOfFields.add(new String(""));
}
add(new ListView<Object>("list", numberOfFields) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(ListItem<Object> item) {
final String word = (String) item.getModelObject();
System.out.println( "word =" +word );
TextField<String> textField = new TextField<String>("word", Model.of(""));
textField.setOutputMarkupId(true);
item.add(textField);
}
});
}
}
};
add(form);
form.add(voucherCodeField);
form.add(button);
}
}
You're trying to assign a Textfield to a <div> element (voucherCode), you need <input type="text"> instead.
You need to add list to your form right away, onSubmit is too late. Just set it outside, similar to button and call myListView.setList when submitting the form.
These are the two things I spotted... if you still have problems please let us know about the error messages you get.