Skip to content

Commit a0725fd

Browse files
committed
Refactor tests to use StoreTestHelpers for store creation and utility registration
- Updated SelectorStoreComponentTests to utilize StoreTestHelpers for creating stores. - Refactored StoreComponentTests to use StoreTestHelpers for store initialization and utility registration. - Enhanced StoreServiceExtensionsTests with new tests for AddStoreWithUtilities and its configurations. - Modified StoreAdvancedTests and StoreTests to leverage StoreTestHelpers for store instantiation. - Improved AsyncHelpersIntegrationTests by registering store utilities using StoreTestHelpers. - Introduced DependencyInjectionTests to validate DI setup for stores and utilities. - Added FullWorkflowTests to ensure multiple stores work independently with proper utility registration. - Updated PerformanceTests to use async updates for multiple stores. - Created StoreTestHelpers class to encapsulate store creation and utility registration logic. - Adjusted DebounceManagerTests to improve action completion verification with TaskCompletionSource.
1 parent 4c073b0 commit a0725fd

40 files changed

Lines changed: 3776 additions & 456 deletions

samples/EasyAppDev.Blazor.Store.Sample/Program.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using EasyAppDev.Blazor.Store.Persistence;
88
using Microsoft.JSInterop;
99
using EasyAppDev.Blazor.Store.Diagnostics;
10+
using EasyAppDev.Blazor.Store.AsyncActions;
11+
using EasyAppDev.Blazor.Store.Utilities;
1012

1113
var builder = WebAssemblyHostBuilder.CreateDefault(args);
1214
builder.RootComponents.Add<App>("#app");
@@ -17,6 +19,23 @@
1719
// Register diagnostics service - only active in DEBUG builds
1820
builder.Services.AddStoreDiagnostics();
1921

22+
// Register utility services required by StoreComponent
23+
// Note: AddStoreUtilities() registers IDebounceManager, IThrottleManager, and ILazyCache
24+
// These services are required dependencies for StoreComponent<T>
25+
builder.Services.AddStoreUtilities();
26+
27+
// Register async action executors for each state type
28+
builder.Services.AddAsyncActionExecutor<CounterState>();
29+
builder.Services.AddAsyncActionExecutor<DebounceState>();
30+
builder.Services.AddAsyncActionExecutor<TodoState>();
31+
builder.Services.AddAsyncActionExecutor<UserProfileState>();
32+
builder.Services.AddAsyncActionExecutor<AsyncDataDemoState>();
33+
builder.Services.AddAsyncActionExecutor<UserManagementState>();
34+
builder.Services.AddAsyncActionExecutor<ShoppingCartState>();
35+
builder.Services.AddAsyncActionExecutor<ThemeState>();
36+
builder.Services.AddAsyncActionExecutor<ProductCatalogState>();
37+
builder.Services.AddAsyncActionExecutor<ComprehensiveDemoState>();
38+
2039
// ============================================================================
2140
// Counter Store - Basic example with DevTools and Logging
2241
// ============================================================================
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (c) EasyAppDev. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using EasyAppDev.Blazor.Store.Core;
5+
using Microsoft.Extensions.Logging;
6+
using System.Runtime.CompilerServices;
7+
8+
namespace EasyAppDev.Blazor.Store.AsyncActions;
9+
10+
/// <summary>
11+
/// Default implementation of <see cref="IAsyncActionExecutor{TState}"/>.
12+
/// </summary>
13+
/// <typeparam name="TState">The type of state being managed.</typeparam>
14+
public sealed class AsyncActionExecutor<TState> : IAsyncActionExecutor<TState> where TState : notnull
15+
{
16+
private readonly IStateWriter<TState> _stateWriter;
17+
private readonly ILogger<AsyncActionExecutor<TState>>? _logger;
18+
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="AsyncActionExecutor{TState}"/> class.
21+
/// </summary>
22+
/// <param name="stateWriter">The state writer for updating state.</param>
23+
/// <param name="logger">Optional logger for error reporting.</param>
24+
public AsyncActionExecutor(
25+
IStateWriter<TState> stateWriter,
26+
ILogger<AsyncActionExecutor<TState>>? logger = null)
27+
{
28+
_stateWriter = stateWriter ?? throw new ArgumentNullException(nameof(stateWriter));
29+
_logger = logger;
30+
}
31+
32+
/// <inheritdoc />
33+
public async Task ExecuteAsync<TResult>(
34+
Func<Task<TResult>> asyncAction,
35+
Func<TState, TState> loading,
36+
Func<TState, TResult, TState> success,
37+
Func<TState, Exception, TState>? error = null,
38+
[CallerMemberName] string? action = null)
39+
{
40+
ArgumentNullException.ThrowIfNull(asyncAction);
41+
ArgumentNullException.ThrowIfNull(loading);
42+
ArgumentNullException.ThrowIfNull(success);
43+
44+
// Set loading state
45+
await _stateWriter.UpdateAsync(loading, $"{action}_LOADING");
46+
47+
try
48+
{
49+
// Execute async action
50+
var result = await asyncAction();
51+
52+
// Set success state
53+
await _stateWriter.UpdateAsync(s => success(s, result), $"{action}_SUCCESS");
54+
}
55+
catch (Exception ex)
56+
{
57+
// Set error state
58+
if (error != null)
59+
{
60+
await _stateWriter.UpdateAsync(s => error(s, ex), $"{action}_ERROR");
61+
}
62+
else
63+
{
64+
// If no error handler provided, log and keep current state
65+
_logger?.LogError(ex, "Error in async action: {Action}", action);
66+
}
67+
}
68+
}
69+
70+
/// <inheritdoc />
71+
public async Task ExecuteAsync(
72+
Func<Task> asyncAction,
73+
Func<TState, TState> loading,
74+
Func<TState, TState> success,
75+
Func<TState, Exception, TState>? error = null,
76+
[CallerMemberName] string? action = null)
77+
{
78+
ArgumentNullException.ThrowIfNull(asyncAction);
79+
ArgumentNullException.ThrowIfNull(loading);
80+
ArgumentNullException.ThrowIfNull(success);
81+
82+
await _stateWriter.UpdateAsync(loading, $"{action}_LOADING");
83+
84+
try
85+
{
86+
await asyncAction();
87+
await _stateWriter.UpdateAsync(success, $"{action}_SUCCESS");
88+
}
89+
catch (Exception ex)
90+
{
91+
if (error != null)
92+
{
93+
await _stateWriter.UpdateAsync(s => error(s, ex), $"{action}_ERROR");
94+
}
95+
else
96+
{
97+
_logger?.LogError(ex, "Error in async action: {Action}", action);
98+
}
99+
}
100+
}
101+
102+
/// <inheritdoc />
103+
public async Task ExecuteAsync<TResult>(
104+
Func<Task<TResult>> asyncAction,
105+
Func<TState, TState> loading,
106+
Func<TState, TState> success,
107+
Func<TState, Exception, TState>? error = null,
108+
[CallerMemberName] string? action = null)
109+
{
110+
await ExecuteAsync(
111+
asyncAction,
112+
loading,
113+
(s, _) => success(s), // Discard result
114+
error,
115+
action);
116+
}
117+
}

0 commit comments

Comments
 (0)