Skip to content

Commit 1a5b717

Browse files
Add some debounce tests (from MudBlazor)
1 parent dbba6f1 commit 1a5b717

6 files changed

Lines changed: 314 additions & 22 deletions

File tree

docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.xml

Lines changed: 174 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/CodeBeam.MudBlazor.Extensions.Docs/CodeBeam.MudBlazor.Extensions.Docs.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<ItemGroup>
1616
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.*" />
17-
<PackageReference Include="MudBlazor" Version="9.0.0" />
17+
<PackageReference Include="MudBlazor" Version="9.3.0" />
1818
</ItemGroup>
1919

2020
<ItemGroup>

src/CodeBeam.MudBlazor.Extensions/Base/MudBaseInputExtended.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,17 @@ protected MudBaseInputExtended()
9494
/// </summary>
9595
[Parameter]
9696
[Category(CategoryTypes.FormComponent.Behavior)]
97-
public bool DisablePaste { get; set; }
97+
public bool DisablePaste { get; set; } = false;
98+
99+
/// <summary>
100+
/// Override to read Value from ParameterState instead of backing field.
101+
/// </summary>
102+
protected internal new T? ReadValue => base.ReadValue;
103+
104+
/// <summary>
105+
/// Override to write Value to ParameterState instead of backing field.
106+
/// </summary>
107+
protected internal new string? ReadText => base.ReadText;
98108

99109
/// <summary>
100110
/// Invokes logic to be executed before input is processed, including raising the BeforeInput event if a

src/CodeBeam.MudBlazor.Extensions/CodeBeam.MudBlazor.Extensions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
</ItemGroup>
4242

4343
<ItemGroup>
44-
<PackageReference Include="MudBlazor" Version="9.0.0" />
44+
<PackageReference Include="MudBlazor" Version="9.3.0" />
4545
</ItemGroup>
4646

4747
<Target Name="MinifyMudExtensionsJs" AfterTargets="Build" Condition="'$(CI)' != 'true'&#xD;&#xA; AND '$(TargetFramework)' == 'net10.0'&#xD;&#xA; AND Exists('TScripts/MudExtensions.js')">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@namespace MudExtensions.UnitTests.TestComponents
2+
3+
<MudTextField T="string"
4+
@bind-Value="_value"
5+
DebounceInterval="500"
6+
Label="Debounced" />
7+
8+
<MudTextField T="string"
9+
@bind-Value="_value"
10+
Label="Immediate" />
11+
12+
@code {
13+
private string? _value = "i";
14+
15+
protected override async Task OnInitializedAsync()
16+
{
17+
await Task.Yield();
18+
_value = "init value";
19+
await InvokeAsync(StateHasChanged);
20+
}
21+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using AwesomeAssertions;
2+
using Bunit;
3+
using Microsoft.AspNetCore.Components;
4+
using MudExtensions.UnitTests.Extensions;
5+
using MudExtensions.UnitTests.TestComponents;
6+
7+
namespace MudExtensions.UnitTests.Components;
8+
9+
[TestFixture]
10+
public class DebouncedInputExtendedTests : BunitTest
11+
{
12+
/// <summary>
13+
/// If Debounce Interval is null or 0, Value should change immediately
14+
/// </summary>
15+
[Test]
16+
public async Task WithNoDebounceIntervalValueShouldChangeImmediately()
17+
{
18+
//no interval passed, so, by default is 0
19+
// We pass the Immediate parameter set to true, in order to bind to oninput
20+
var comp = Context.Render<MudTextFieldExtended<string>>(parameters => parameters.Add(p => p.Immediate, true));
21+
var textField = comp.Instance;
22+
var input = comp.Find("input");
23+
24+
//Act
25+
await input.InputAsync(new ChangeEventArgs() { Value = "Some Value" });
26+
27+
//Assert
28+
//input value has changed, DebounceInterval is 0, so Value should change in TextField immediately
29+
textField.ReadValue.Should().Be("Some Value");
30+
}
31+
32+
/// <summary>
33+
/// Value should not change immediately. Should respect the Debounce Interval
34+
/// </summary>
35+
[Test]
36+
public async Task ShouldRespectDebounceIntervalPropertyInTextField()
37+
{
38+
var comp = Context.Render<MudTextFieldExtended<string>>(parameters => parameters.Add(p => p.DebounceInterval, 200d));
39+
var textField = comp.Instance;
40+
var input = comp.Find("input");
41+
42+
//Act
43+
await input.InputAsync(new ChangeEventArgs() { Value = "Some Value" });
44+
45+
//Assert
46+
//if DebounceInterval is set, Immediate should be true by default
47+
textField.Immediate.Should().BeTrue();
48+
49+
//input value has changed, but elapsed time is 0, so Value should not change in TextField
50+
textField.ReadValue.Should().BeNull();
51+
52+
//DebounceInterval is 200 ms, so at 100 ms Value should not change in TextField
53+
await Task.Delay(100);
54+
textField.ReadValue.Should().BeNull();
55+
56+
//More than 200 ms had elapsed, so Value should be updated
57+
await comp.WaitForAssertionAsync(() => textField.ReadValue.Should().Be("Some Value"));
58+
}
59+
60+
/// <summary>
61+
/// DebounceInterval updates with epsilon-equivalent values should not break debouncing
62+
/// </summary>
63+
[Test]
64+
public async Task DebounceInterval_EpsilonEquivalentValues_PreservesDebounce()
65+
{
66+
// Arrange
67+
var comp = Context.Render<MudTextFieldExtended<string>>(parameters => parameters.Add(p => p.DebounceInterval, 200.0));
68+
var textField = comp.Instance;
69+
var input = comp.Find("input");
70+
71+
// Act - Input a value
72+
await input.InputAsync(new ChangeEventArgs() { Value = "Test Value" });
73+
74+
// Change DebounceInterval to an epsilon-equivalent value (should not reset debouncer)
75+
await comp.SetParametersAndRenderAsync(parameters => parameters.Add(p => p.DebounceInterval, 200.0000001));
76+
77+
// Assert - Value should still be null (debounce still pending)
78+
textField.ReadValue.Should().BeNull();
79+
80+
// Wait for the debounce to complete
81+
await comp.WaitForAssertionAsync(() => textField.ReadValue.Should().Be("Test Value"));
82+
}
83+
84+
[Test]
85+
public async Task DebouncedTextField_ShouldStayInSyncWithBoundValueAfterAsyncInitialization()
86+
{
87+
var comp = Context.Render<DebouncedTextFieldAsyncInitializationSyncTest>();
88+
89+
await comp.WaitForAssertionAsync(() =>
90+
{
91+
var inputs = comp.FindAll("input");
92+
inputs[0].GetAttribute("value").Should().Be("init value");
93+
inputs[1].GetAttribute("value").Should().Be("init value");
94+
});
95+
96+
var immediateInput = comp.FindAll("input")[1];
97+
await immediateInput.ChangeAsync(new ChangeEventArgs { Value = "changed value" });
98+
99+
await comp.WaitForAssertionAsync(() =>
100+
{
101+
var inputs = comp.FindAll("input");
102+
inputs[0].GetAttribute("value").Should().Be("changed value");
103+
inputs[1].GetAttribute("value").Should().Be("changed value");
104+
}, TimeSpan.FromSeconds(1));
105+
}
106+
}

0 commit comments

Comments
 (0)