-
-
Notifications
You must be signed in to change notification settings - Fork 119
Expand file tree
/
Copy pathFakeNavigationManager.cs
More file actions
155 lines (131 loc) · 4.44 KB
/
FakeNavigationManager.cs
File metadata and controls
155 lines (131 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using Bunit.Rendering;
using Microsoft.AspNetCore.Components.Routing;
namespace Bunit.TestDoubles;
using URI = Uri;
/// <summary>
/// Represents a fake <see cref="NavigationManager"/> that captures calls to
/// <see cref="NavigationManager.NavigateTo(string, bool)"/> for testing purposes.
/// </summary>
public sealed class FakeNavigationManager : NavigationManager
{
private readonly TestContextBase testContextBase;
private readonly Stack<NavigationHistory> history = new();
/// <summary>
/// The navigation history captured by the <see cref="FakeNavigationManager"/>.
/// This is a stack based collection, so the first element is the latest/current navigation target.
/// </summary>
/// <remarks>
/// The initial Uri is not added to the history.
/// </remarks>
public IReadOnlyCollection<NavigationHistory> History => history;
/// <summary>
/// Initializes a new instance of the <see cref="FakeNavigationManager"/> class.
/// </summary>
[SuppressMessage("Minor Code Smell", "S1075:URIs should not be hardcoded", Justification = "By design. Fake navigation manager defaults to local host as base URI.")]
public FakeNavigationManager(TestContextBase testContextBase)
{
this.testContextBase = testContextBase;
Initialize("http://localhost/", "http://localhost/");
}
#if !NET6_0_OR_GREATER
/// <inheritdoc/>
protected override void NavigateToCore(string uri, bool forceLoad)
{
var absoluteUri = GetNewAbsoluteUri(uri);
var changedBaseUri = HasDifferentBaseUri(absoluteUri);
if (changedBaseUri)
{
BaseUri = GetBaseUri(absoluteUri);
}
Uri = ToAbsoluteUri(uri).OriginalString;
history.Push(new NavigationHistory(uri, new NavigationOptions(forceLoad)));
testContextBase.Renderer.Dispatcher.InvokeAsync(() =>
{
Uri = absoluteUri.OriginalString;
// Only notify of changes if user navigates within the same
// base url (domain). Otherwise, the user navigated away
// from the app, and Blazor's NavigationManager would
// not notify of location changes.
if (!changedBaseUri)
{
NotifyLocationChanged(isInterceptedLink: false);
}
else
{
BaseUri = GetBaseUri(absoluteUri);
}
});
}
#endif
#if NET6_0_OR_GREATER
/// <inheritdoc/>
protected override void NavigateToCore(string uri, NavigationOptions options)
{
var absoluteUri = GetNewAbsoluteUri(uri);
var changedBaseUri = HasDifferentBaseUri(absoluteUri);
if (options.ReplaceHistoryEntry && history.Count > 0)
history.Pop();
#if NET7_0_OR_GREATER
HistoryEntryState = options.ForceLoad ? null : options.HistoryEntryState;
testContextBase.Renderer.Dispatcher.InvokeAsync(async () =>
#else
testContextBase.Renderer.Dispatcher.InvokeAsync(() =>
#endif
{
#if NET7_0_OR_GREATER
var shouldContinueNavigation = false;
try
{
shouldContinueNavigation = await NotifyLocationChangingAsync(uri, options.HistoryEntryState, isNavigationIntercepted: false).ConfigureAwait(false);
}
catch (Exception exception)
{
history.Push(new NavigationHistory(uri, options, NavigationState.Faulted, exception));
return;
}
history.Push(new NavigationHistory(uri, options, shouldContinueNavigation ? NavigationState.Succeeded : NavigationState.Prevented));
if (!shouldContinueNavigation)
{
return;
}
#else
history.Push(new NavigationHistory(uri, options));
#endif
if (changedBaseUri)
{
BaseUri = GetBaseUri(absoluteUri);
}
Uri = absoluteUri.OriginalString;
// Only notify of changes if user navigates within the same
// base url (domain). Otherwise, the user navigated away
// from the app, and Blazor's NavigationManager would
// not notify of location changes.
if (!changedBaseUri)
{
NotifyLocationChanged(isInterceptedLink: false);
}
});
}
#endif
#if NET7_0_OR_GREATER
/// <inheritdoc/>
protected override void SetNavigationLockState(bool value) { }
/// <inheritdoc/>
protected override void HandleLocationChangingHandlerException(Exception ex, LocationChangingContext context)
=> throw ex;
#endif
private URI GetNewAbsoluteUri(string uri)
=> new URI(uri, UriKind.RelativeOrAbsolute).IsAbsoluteUri
? new URI(uri, UriKind.RelativeOrAbsolute) : ToAbsoluteUri(uri);
private bool HasDifferentBaseUri(URI absoluteUri)
=> URI.Compare(
new URI(BaseUri, UriKind.Absolute),
absoluteUri,
UriComponents.SchemeAndServer,
UriFormat.Unescaped,
StringComparison.OrdinalIgnoreCase) != 0;
private static string GetBaseUri(URI uri)
{
return uri.Scheme + "://" + uri.Authority + "/";
}
}