-
-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Navigation will set parameters
- Loading branch information
1 parent
0ed621c
commit c0bfe13
Showing
14 changed files
with
367 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace Bunit.Rendering; | ||
|
||
/// <summary> | ||
/// This internal class is used to keep track of all components that have been rendered. | ||
/// This class is not intended to be used directly by users of bUnit. | ||
/// </summary> | ||
public sealed class ComponentRegistry | ||
{ | ||
private readonly HashSet<IComponent> components = []; | ||
|
||
/// <summary> | ||
/// Retrieves all components that have been rendered. | ||
/// </summary> | ||
public ISet<IComponent> Components => components; | ||
|
||
/// <summary> | ||
/// Registers a component as rendered. | ||
/// </summary> | ||
public void Register(IComponent component) | ||
=> components.Add(component); | ||
|
||
/// <summary> | ||
/// Removes all components from the registry. | ||
/// </summary> | ||
public void Clear() => components.Clear(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
using System.Globalization; | ||
using System.Reflection; | ||
using Bunit.Rendering; | ||
using Microsoft.AspNetCore.Components.Routing; | ||
|
||
namespace Bunit.TestDoubles.Router; | ||
|
||
internal sealed class FakeRouter : IDisposable | ||
{ | ||
private readonly NavigationManager navigationManager; | ||
private readonly ComponentRegistry componentRegistry; | ||
|
||
public FakeRouter(NavigationManager navigationManager, ComponentRegistry componentRegistry) | ||
{ | ||
this.navigationManager = navigationManager; | ||
this.componentRegistry = componentRegistry; | ||
navigationManager.LocationChanged += UpdatePageParameters; | ||
} | ||
|
||
public void Dispose() => navigationManager.LocationChanged -= UpdatePageParameters; | ||
|
||
private void UpdatePageParameters(object? sender, LocationChangedEventArgs e) | ||
{ | ||
var uri = new Uri(e.Location); | ||
var relativeUri = uri.PathAndQuery; | ||
|
||
foreach (var instance in componentRegistry.Components) | ||
{ | ||
var routeAttributes = GetRouteAttributesFromComponent(instance); | ||
|
||
if (routeAttributes.Length == 0) | ||
{ | ||
continue; | ||
} | ||
|
||
foreach (var template in routeAttributes.Select(r => r.Template)) | ||
{ | ||
var templateSegments = template.Trim('/').Split("/"); | ||
var uriSegments = relativeUri.Trim('/').Split("/"); | ||
|
||
if (templateSegments.Length > uriSegments.Length) | ||
{ | ||
continue; | ||
} | ||
#if NET6_0_OR_GREATER | ||
var parameters = new Dictionary<string, object?>(); | ||
#else | ||
var parameters = new Dictionary<string, object>(); | ||
#endif | ||
|
||
for (var i = 0; i < templateSegments.Length; i++) | ||
{ | ||
var templateSegment = templateSegments[i]; | ||
if (templateSegment.StartsWith('{') && templateSegment.EndsWith('}')) | ||
{ | ||
var parameterName = GetParameterName(templateSegment); | ||
var property = GetParameterProperty(instance, parameterName); | ||
|
||
if (property is null) | ||
{ | ||
continue; | ||
} | ||
|
||
var isCatchAllParameter = templateSegment[1] == '*'; | ||
if (!isCatchAllParameter) | ||
{ | ||
parameters[property.Name] = Convert.ChangeType(uriSegments[i], property.PropertyType, | ||
CultureInfo.InvariantCulture); | ||
} | ||
else | ||
{ | ||
parameters[parameterName] = string.Join("/", uriSegments.Skip(i)); | ||
} | ||
} | ||
else if (templateSegment != uriSegments[i]) | ||
{ | ||
break; | ||
} | ||
} | ||
|
||
if (parameters.Count == 0) | ||
{ | ||
continue; | ||
} | ||
|
||
// Shall we await this? This should be synchronous in most cases | ||
// If not, very likely the user has overriden the SetParametersAsync method | ||
// And should use WaitForXXX methods to await the desired state | ||
instance.SetParametersAsync(ParameterView.FromDictionary(parameters)); | ||
} | ||
} | ||
} | ||
|
||
private static RouteAttribute[] GetRouteAttributesFromComponent(IComponent instance) | ||
{ | ||
var routeAttributes = instance | ||
.GetType() | ||
.GetCustomAttributes(typeof(RouteAttribute), true) | ||
.Cast<RouteAttribute>() | ||
.ToArray(); | ||
return routeAttributes; | ||
} | ||
|
||
private static string GetParameterName(string templateSegment) => templateSegment.Trim('{', '}', '*').Split(':')[0]; | ||
|
||
private static PropertyInfo? GetParameterProperty(object instance, string propertyName) | ||
{ | ||
var propertyInfos = instance.GetType() | ||
.GetProperties(BindingFlags.Public | BindingFlags.Instance); | ||
|
||
return Array.Find(propertyInfos, prop => prop.GetCustomAttributes(typeof(ParameterAttribute), true).Any() && | ||
string.Equals(prop.Name, propertyName, StringComparison.OrdinalIgnoreCase)); | ||
} | ||
} |
Oops, something went wrong.