Skip to content

Commit 1340828

Browse files
Added tests for disabled and cleaned up test project
1 parent 628ef85 commit 1340828

File tree

9 files changed

+429
-237
lines changed

9 files changed

+429
-237
lines changed

src/SimpleBlazorMultiselect.Demo/Layout/NavMenu.razor

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,12 @@
7676
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> ObjectBinding
7777
</NavLink>
7878
</div>
79+
80+
<!-- DisabledDropdown -->
81+
<div class="nav-item px-3">
82+
<NavLink class="nav-link" href="DisabledDropdown" Match="NavLinkMatch.All">
83+
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> DisabledDropdown
84+
</NavLink>
85+
</div>
7986
</nav>
8087
</div>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@page "/DisabledDropdown"
2+
<h3>DisabledDropdown</h3>
3+
4+
<div class="row">
5+
<div class="col-4">
6+
<SimpleMultiselect
7+
Disabled="@_isDisabled"
8+
Options="@Globals.EuropeanCapitals"
9+
CanFilter="true"
10+
@bind-SelectedOptions="_selectedItems"/>
11+
</div>
12+
<div class="col-4">
13+
<button class="btn btn-primary" @onclick="() => _isDisabled = !_isDisabled">
14+
@(_isDisabled ? "Enable" : "Disable")
15+
</button>
16+
<br/>
17+
You have selected the following items:
18+
<ul>
19+
@foreach (var item in _selectedItems)
20+
{
21+
<li>@item</li>
22+
}
23+
</ul>
24+
</div>
25+
</div>
26+
27+
@code {
28+
private bool _isDisabled;
29+
30+
private HashSet<string> _selectedItems = [];
31+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using Bunit;
2+
using FluentAssertions;
3+
using Xunit;
4+
5+
namespace SimpleBlazorMultiselect.Tests;
6+
7+
public class DisabledTests : BaseTest
8+
{
9+
[Fact]
10+
public void Component_WhenDisabled_HasDisabledAttribute()
11+
{
12+
var component = RenderComponent<SimpleMultiselect<string>>(parameters => parameters
13+
.Add(p => p.Options, TestOptions)
14+
.Add(p => p.Disabled, true)
15+
);
16+
17+
var button = component.Find("button");
18+
button.HasAttribute("disabled").Should().BeTrue();
19+
}
20+
21+
[Fact]
22+
public void Component_WhenDisabled_DoesNotOpenOnClick()
23+
{
24+
var component = RenderComponent<SimpleMultiselect<string>>(parameters => parameters
25+
.Add(p => p.Options, TestOptions)
26+
.Add(p => p.Disabled, true)
27+
);
28+
29+
var button = component.Find("button");
30+
button.Click();
31+
32+
AssertClosed(component);
33+
}
34+
35+
[Fact]
36+
public void Component_WhenEnabled_OpensOnClick()
37+
{
38+
var component = RenderComponent<SimpleMultiselect<string>>(parameters => parameters
39+
.Add(p => p.Options, TestOptions)
40+
.Add(p => p.Disabled, false)
41+
);
42+
43+
var button = component.Find("button");
44+
button.Click();
45+
46+
AssertOpen(component, TestOptions.Count);
47+
}
48+
49+
[Fact]
50+
public void Component_WhenDisabledThenEnabled_OpensOnClick()
51+
{
52+
var component = RenderComponent<SimpleMultiselect<string>>(parameters => parameters
53+
.Add(p => p.Options, TestOptions)
54+
.Add(p => p.Disabled, true)
55+
);
56+
57+
var button = component.Find("button");
58+
button.Click();
59+
AssertClosed(component);
60+
61+
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, false));
62+
63+
button.Click();
64+
AssertOpen(component, TestOptions.Count);
65+
}
66+
67+
[Fact]
68+
public void Component_WhenEnabledThenDisabled_DoesNotOpenOnClick()
69+
{
70+
var component = RenderComponent<SimpleMultiselect<string>>(parameters => parameters
71+
.Add(p => p.Options, TestOptions)
72+
.Add(p => p.Disabled, false)
73+
);
74+
75+
var button = component.Find("button");
76+
button.Click();
77+
AssertOpen(component, TestOptions.Count);
78+
79+
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
80+
81+
button.Click();
82+
AssertClosed(component);
83+
}
84+
85+
[Fact]
86+
public void Component_WhenDisabledWithMenuOpen_ClosesMenu()
87+
{
88+
var component = RenderComponent<SimpleMultiselect<string>>(parameters => parameters
89+
.Add(p => p.Options, TestOptions)
90+
);
91+
92+
var button = component.Find("button");
93+
button.Click();
94+
AssertOpen(component, TestOptions.Count);
95+
96+
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
97+
98+
AssertClosed(component);
99+
}
100+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using AngleSharp.Dom;
2+
using AngleSharp.Html.Dom;
3+
using Bunit;
4+
using FluentAssertions;
5+
using Microsoft.AspNetCore.Components;
6+
using Xunit;
7+
8+
namespace SimpleBlazorMultiselect.Tests;
9+
10+
public class EqualityTests : BaseTest
11+
{
12+
[Fact]
13+
public void Component_CanDeselect_WhenPrefilledValueItems()
14+
{
15+
var options = new List<TestValueItem>
16+
{
17+
new("1", "Apple"),
18+
new("2", "Banana"),
19+
new("3", "Cherry")
20+
};
21+
var selectedItems = new HashSet<TestValueItem>
22+
{
23+
new("1", "Apple")
24+
};
25+
26+
var component = RenderComponent<SimpleMultiselect<TestValueItem>>(parameters => parameters
27+
.Add(p => p.Options, options)
28+
.Add(p => p.SelectedOptions, selectedItems)
29+
.Add(p => p.StringSelector, item => item.Name)
30+
.Add(p => p.DefaultText, "Select fruits")
31+
.Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create<HashSet<TestValueItem>>(this, newSelection => { selectedItems = newSelection; })));
32+
33+
var button = component.Find("button");
34+
button.TextContent.Should().Contain("Apple");
35+
button.Click();
36+
37+
// Now only apple should be checked
38+
var appleOption = component.FindAll(".dropdown-item")[0];
39+
var appleCheckbox = appleOption.QuerySelector<IHtmlInputElement>("input[type='checkbox']");
40+
appleCheckbox.Should().NotBeNull();
41+
appleCheckbox.IsChecked.Should().BeTrue();
42+
43+
appleOption.Click();
44+
45+
// After clicking, apple should be deselected
46+
selectedItems.Should().BeEmpty();
47+
button = component.Find("button");
48+
button.TextContent.Should().Be("Select fruits");
49+
}
50+
51+
[Fact]
52+
public void Component_CanDeselect_WhenPrefilledReferenceItems()
53+
{
54+
var options = new List<TestReferenceItem>
55+
{
56+
new("1", "Apple"),
57+
new("2", "Banana"),
58+
new("3", "Cherry")
59+
};
60+
var selectedItems = new HashSet<TestReferenceItem>
61+
{
62+
new("1", "Apple")
63+
};
64+
65+
var component = RenderComponent<SimpleMultiselect<TestReferenceItem>>(parameters => parameters
66+
.Add(p => p.Options, options)
67+
.Add(p => p.SelectedOptions, selectedItems)
68+
.Add(p => p.StringSelector, item => item.Name)
69+
.Add(p => p.DefaultText, "Select fruits")
70+
.Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create<HashSet<TestReferenceItem>>(this, newSelection => { selectedItems = newSelection; })));
71+
72+
var button = component.Find("button");
73+
button.TextContent.Should().Contain("Apple");
74+
button.Click();
75+
76+
// Now only apple should be checked
77+
var appleOption = component.FindAll(".dropdown-item")[0];
78+
var appleCheckbox = appleOption.QuerySelector<IHtmlInputElement>("input[type='checkbox']");
79+
appleCheckbox.Should().NotBeNull();
80+
appleCheckbox.IsChecked.Should().BeTrue();
81+
82+
appleOption.Click();
83+
84+
// After clicking, apple should be deselected
85+
selectedItems.Should().BeEmpty();
86+
button = component.Find("button");
87+
button.TextContent.Should().Be("Select fruits");
88+
}
89+
90+
[Fact]
91+
public void Component_CanDeselectValueItem_WhenMatchByReference()
92+
{
93+
var options = new List<TestValueItem>
94+
{
95+
new("1", "Apple"),
96+
new("2", "Banana"),
97+
new("3", "Cherry")
98+
};
99+
var selectedItems = new HashSet<TestValueItem>
100+
{
101+
new("1", "Apple")
102+
};
103+
104+
var component = RenderComponent<SimpleMultiselect<TestValueItem>>(parameters => parameters
105+
.Add(p => p.Options, options)
106+
.Add(p => p.SelectedOptions, selectedItems)
107+
.Add(p => p.StringSelector, item => item.Name)
108+
.Add(p => p.DefaultText, "Select fruits")
109+
.Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create<HashSet<TestValueItem>>(this, newSelection => { selectedItems = newSelection; }))
110+
.Add(p => p.MatchByReference, true)); // Should not matter for value types
111+
112+
var button = component.Find("button");
113+
button.TextContent.Should().Contain("Apple");
114+
button.Click();
115+
116+
// Now only apple should be checked
117+
var appleOption = component.FindAll(".dropdown-item")[0];
118+
var appleCheckbox = appleOption.QuerySelector<IHtmlInputElement>("input[type='checkbox']");
119+
appleCheckbox.Should().NotBeNull();
120+
appleCheckbox.IsChecked.Should().BeTrue();
121+
122+
appleOption.Click();
123+
124+
// After clicking, apple should be deselected
125+
selectedItems.Should().BeEmpty();
126+
button = component.Find("button");
127+
button.TextContent.Should().Be("Select fruits");
128+
}
129+
130+
[Fact]
131+
public void Component_CannotDeselectIdenticalInstance_WhenMatchByReference()
132+
{
133+
var options = new List<TestReferenceItem>
134+
{
135+
new("1", "Apple"),
136+
new("2", "Banana"),
137+
new("3", "Cherry")
138+
};
139+
var selectedItems = new HashSet<TestReferenceItem>
140+
{
141+
new("1", "Apple")
142+
};
143+
144+
var component = RenderComponent<SimpleMultiselect<TestReferenceItem>>(parameters => parameters
145+
.Add(p => p.Options, options)
146+
.Add(p => p.SelectedOptions, selectedItems)
147+
.Add(p => p.StringSelector, item => item.Name)
148+
.Add(p => p.DefaultText, "Select fruits")
149+
.Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create<HashSet<TestReferenceItem>>(this, newSelection => { selectedItems = newSelection; }))
150+
.Add(p => p.MatchByReference, true)); // This will break the deselection
151+
152+
var button = component.Find("button");
153+
button.TextContent.Should().Contain("Apple");
154+
button.Click();
155+
156+
// Apple should not be checked because the instance is different
157+
// So clicking it will add another apple instead of removing the existing one
158+
var appleOption = component.FindAll(".dropdown-item")[0];
159+
var appleCheckbox = appleOption.QuerySelector<IHtmlInputElement>("input[type='checkbox']");
160+
appleCheckbox.Should().NotBeNull();
161+
appleCheckbox.IsChecked.Should().BeFalse();
162+
163+
appleOption.Click();
164+
165+
// After clicking, we should have two apples
166+
selectedItems.Should().HaveCount(2);
167+
button = component.Find("button");
168+
button.TextContent.Should().Be("Apple, Apple");
169+
}
170+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Bunit;
2+
using FluentAssertions;
3+
4+
namespace SimpleBlazorMultiselect.Tests;
5+
6+
public class BaseTest : TestContext
7+
{
8+
protected readonly List<string> TestOptions =
9+
[
10+
"Apple",
11+
"Banana",
12+
"Cherry",
13+
"Date",
14+
"Elderberry"
15+
];
16+
17+
public BaseTest()
18+
{
19+
JSInterop.SetupModule("./_content/SimpleBlazorMultiselect/js/simpleMultiselect.js")
20+
.SetupModule("register", invocation => invocation.Arguments.Count == 2)
21+
.SetupVoid("dispose");
22+
}
23+
24+
protected static void AssertOpen(IRenderedComponent<SimpleMultiselect<string>> component, int? expectedItemCount = null)
25+
{
26+
var dropdown = component.Find(".dropdown-menu.show");
27+
dropdown.Should().NotBeNull();
28+
29+
if (expectedItemCount.HasValue)
30+
{
31+
var dropdownItems = component.FindAll(".dropdown-item");
32+
dropdownItems.Should().HaveCount(expectedItemCount.Value);
33+
}
34+
}
35+
36+
protected static void AssertClosed(IRenderedComponent<SimpleMultiselect<string>> component)
37+
{
38+
var dropdown = component.FindAll(".dropdown-menu.show");
39+
dropdown.Should().BeEmpty();
40+
41+
var dropdownItems = component.FindAll(".dropdown-item");
42+
dropdownItems.Should().HaveCount(0);
43+
}
44+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace SimpleBlazorMultiselect.Tests;
2+
3+
public class TestReferenceItem(string id, string name)
4+
{
5+
public string Id { get; set; } = id;
6+
public string Name { get; set; } = name;
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace SimpleBlazorMultiselect.Tests;
2+
3+
public record TestValueItem(string Id, string Name);

0 commit comments

Comments
 (0)