diff --git a/src/BootstrapBlazor/Components/Timer/Timer.razor b/src/BootstrapBlazor/Components/Timer/Timer.razor
index ffc51e67463e3becaa8db921cad936b354395650..f36ac98ff776fa3cdfc8a11e708a9740aa1a17be 100644
--- a/src/BootstrapBlazor/Components/Timer/Timer.razor
+++ b/src/BootstrapBlazor/Components/Timer/Timer.razor
@@ -19,6 +19,6 @@
}
else
{
-
+
}
-
\ No newline at end of file
+
diff --git a/src/BootstrapBlazor/Components/Timer/Timer.razor.cs b/src/BootstrapBlazor/Components/Timer/Timer.razor.cs
index 0fc64c911bc89244525a6ba04fe4c0973fbaff3d..c9d1ad3d1e9cde1b0b62b7f1a12dbd515183ca6b 100644
--- a/src/BootstrapBlazor/Components/Timer/Timer.razor.cs
+++ b/src/BootstrapBlazor/Components/Timer/Timer.razor.cs
@@ -42,7 +42,7 @@ public partial class Timer : IDisposable
private CancellationTokenSource? CancelTokenSource { get; set; }
- private ManualResetEvent ResetEvent { get; set; } = new(true);
+ private ManualResetEvent ResetEvent { get; } = new(true);
private bool Vibrate { get; set; }
@@ -153,44 +153,49 @@ public partial class Timer : IDisposable
Task.Run(async () =>
{
+ // 点击 Cancel 后重新设置再点击 Star
if (CancelTokenSource != null)
{
+ CancelTokenSource.Cancel();
CancelTokenSource.Dispose();
}
CancelTokenSource = new CancellationTokenSource();
- try
+ do
{
- do
+ try
{
- await Task.Delay(1000, CancelTokenSource?.Token ?? new CancellationToken(true));
+ await Task.Delay(1000, CancelTokenSource.Token);
+ }
+ catch (TaskCanceledException) { }
- if (!(CancelTokenSource?.IsCancellationRequested ?? true))
- {
- ResetEvent.WaitOne();
- CurrentTimespan = CurrentTimespan.Subtract(TimeSpan.FromSeconds(1));
- await InvokeAsync(StateHasChanged);
- }
+ if (!CancelTokenSource.IsCancellationRequested)
+ {
+ ResetEvent.WaitOne();
+ CurrentTimespan = CurrentTimespan.Subtract(TimeSpan.FromSeconds(1));
+ await InvokeAsync(StateHasChanged);
}
- while (!(CancelTokenSource?.IsCancellationRequested ?? true) && CurrentTimespan > TimeSpan.Zero);
+ }
+ while (!CancelTokenSource.IsCancellationRequested && CurrentTimespan > TimeSpan.Zero);
- if (CurrentTimespan == TimeSpan.Zero)
+ if (CurrentTimespan == TimeSpan.Zero)
+ {
+ await Task.Delay(500, CancelTokenSource.Token);
+ if (!CancelTokenSource.IsCancellationRequested)
{
- await Task.Delay(500, CancelTokenSource?.Token ?? new CancellationToken(true));
- if (!(CancelTokenSource?.IsCancellationRequested ?? true))
+ Value = TimeSpan.Zero;
+ await InvokeAsync(() =>
{
- Value = TimeSpan.Zero;
- await InvokeAsync(() =>
+ Vibrate = IsVibrate;
+ StateHasChanged();
+ if (OnTimeout != null)
{
- Vibrate = IsVibrate;
- StateHasChanged();
- OnTimeout?.Invoke();
- });
- }
+ OnTimeout();
+ }
+ });
}
}
- catch (TaskCanceledException) { }
});
}
@@ -214,8 +219,14 @@ public partial class Timer : IDisposable
private async Task OnClickCancel()
{
Value = TimeSpan.Zero;
- CancelTokenSource?.Cancel();
- if (OnCancel != null) await OnCancel.Invoke();
+ if (CancelTokenSource != null)
+ {
+ CancelTokenSource.Cancel();
+ }
+ if (OnCancel != null)
+ {
+ await OnCancel();
+ }
}
///
@@ -226,9 +237,12 @@ public partial class Timer : IDisposable
{
if (disposing)
{
- CancelTokenSource?.Dispose();
- CancelTokenSource = null;
-
+ if (CancelTokenSource != null)
+ {
+ CancelTokenSource.Cancel();
+ CancelTokenSource.Dispose();
+ CancelTokenSource = null;
+ }
ResetEvent.Dispose();
}
}
diff --git a/test/UnitTest/Components/TimerTest.cs b/test/UnitTest/Components/TimerTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..26ed13a9e9603ceadd423f3df3c382f97ad440a0
--- /dev/null
+++ b/test/UnitTest/Components/TimerTest.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+// Website: https://www.blazor.zone or https://argozhang.github.io/
+
+using System.Threading;
+using Timer = BootstrapBlazor.Components.Timer;
+
+namespace UnitTest.Components;
+
+public class TimerTest : BootstrapBlazorTestBase
+{
+ [Fact]
+ public void Value_Ok()
+ {
+ var cut = Context.RenderComponent(builder => builder.Add(a => a.Value, TimeSpan.FromSeconds(10)));
+
+ Assert.Contains("circle-body", cut.Markup);
+
+ cut.SetParametersAndRender(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.Zero);
+ });
+ Assert.DoesNotContain("circle-body", cut.Markup);
+ }
+
+ [Fact]
+ public void Width_Ok()
+ {
+ var cut = Context.RenderComponent(builder =>
+ {
+ builder.Add(a => a.Value, TimeSpan.FromSeconds(5));
+ builder.Add(a => a.Width, 100);
+ });
+
+ Assert.Contains("width: 100px", cut.Markup);
+ }
+
+ [Fact]
+ public void StrokeWidth_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromSeconds(5));
+ pb.Add(a => a.StrokeWidth, 5);
+ });
+ Assert.Contains("stroke-width=\"5\"", cut.Markup);
+
+ // 增加代码覆盖率
+ //Width / 2 < StrokeWidth
+ cut.SetParametersAndRender(pb =>
+ {
+ pb.Add(a => a.Width, 6);
+ pb.Add(a => a.StrokeWidth, 6);
+ });
+ Assert.Equal(2, cut.Instance.StrokeWidth);
+ }
+
+ [Fact]
+ public void Color_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromSeconds(5));
+ pb.Add(a => a.Color, Color.Success);
+ });
+ Assert.Contains("circle-success", cut.Markup);
+ }
+
+ [Fact]
+ public void ShowProgress_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromSeconds(5));
+ pb.Add(a => a.ShowProgress, false);
+ });
+ Assert.Contains("circle-title d-none", cut.Markup);
+ }
+
+ [Fact]
+ public void IsVibrate_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromSeconds(5));
+ pb.Add(a => a.IsVibrate, false);
+ });
+ }
+
+ [Fact]
+ public async Task OnStart_Ok()
+ {
+ var timeout = false;
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.OnTimeout, () =>
+ {
+ timeout = true;
+ return Task.CompletedTask;
+ });
+ });
+ var downs = cut.FindAll(".time-spinner-arrow.fa-angle-down");
+ downs[2].Click();
+ cut.Find(".time-panel-btn.confirm").Click();
+
+ await Task.Delay(2000);
+ Assert.True(timeout);
+ }
+
+ [Fact]
+ public async Task OnCancel_Ok()
+ {
+ var cancelled = false;
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.OnCancel, () =>
+ {
+ cancelled = true;
+ return Task.CompletedTask;
+ });
+ });
+ var downs = cut.FindAll(".time-spinner-arrow.fa-angle-down");
+ downs[0].Click();
+ cut.Find(".time-panel-btn.confirm").Click();
+ await Task.Delay(1000);
+ var buttons = cut.FindAll(".timer-buttons button");
+ // pause
+ Assert.True(buttons[1].ClassList.Contains("btn-warning"));
+ buttons[1].Click();
+
+ // resume
+ buttons = cut.FindAll(".timer-buttons button");
+ Assert.True(buttons[1].ClassList.Contains("btn-success"));
+ buttons[1].Click();
+
+ // cancel
+ buttons = cut.FindAll(".timer-buttons button");
+ buttons[0].Click();
+ Assert.True(cancelled);
+
+ // 代码覆盖率 Cancel 后再 Star
+ downs = cut.FindAll(".time-spinner-arrow.fa-angle-down");
+ downs[0].Click();
+ cut.Find(".time-panel-btn.confirm").Click();
+ await Task.Delay(1000);
+ }
+}