Skip to content

Commit 72fae79

Browse files
authored
Merge pull request #889 from bUnit-dev/release/v1.11
Release of new minor version v1.11
2 parents b175d86 + 2b2d98f commit 72fae79

File tree

18 files changed

+189
-125
lines changed

18 files changed

+189
-125
lines changed

.github/workflows/verification-dotnet-nightly.yml

Lines changed: 0 additions & 64 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ All notable changes to **bUnit** will be documented in this file. The project ad
66

77
## [Unreleased]
88

9+
### Added
10+
- Added the `StateFromJson` method to the `NavigationHistory` type, to make it easy to deserialize navigation state stored as JSON during a call to `NavigationManager.NavigateTo`, e.g. as seen with the new `InteractiveRequestOptions` type available in .NET 7. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil).
11+
912
## [1.10.14] - 2022-09-16
1013

1114
### Added

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<!-- Shared code analyzers used for all projects in the solution -->
4949
<ItemGroup Label="Code Analyzers">
5050
<PackageReference Include="AsyncFixer" Version="1.6.0" PrivateAssets="All" />
51-
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.44.0.52574" PrivateAssets="All" />
51+
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.46.0.54807" PrivateAssets="All" />
5252
</ItemGroup>
5353

5454
<ItemGroup Label="Implicit usings"

docs/site/docs/test-doubles/fake-navigation-manager.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,32 @@ navMan.NavigateTo("/counter");
136136
var navigationHistory = navMan.History.Single();
137137
Assert.Equal(NavigationState.Faulted, navigationHistory.NavigationState);
138138
Assert.NotNull(navigationHistory.Exception);
139+
```
140+
141+
## Getting the result of `NavigationManager.NavigateToLogin`
142+
[`NavigationManager.NavigateToLogin`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.navigationmanager.navigateto?view=aspnetcore-7.0) is a function, which was introduced with .NET 7, which allows to login dynamically. The function can also retrieve an `InteractiveRequestOptions` object, which can hold additional parameter.
143+
144+
```csharp
145+
InteractiveRequestOptions requestOptions = new()
146+
{
147+
Interaction = InteractionType.SignIn,
148+
ReturnUrl = NavigationManager.Uri,
149+
};
150+
requestOptions.TryAddAdditionalParameter("prompt", "login");
151+
NavigationManager.NavigateToLogin("authentication/login", requestOptions);
152+
```
153+
154+
A test could look like this:
155+
```csharp
156+
using var ctx = new TestContext();
157+
var navigationManager = ctx.Services.GetRequiredService<FakeNavigationManager>();
158+
159+
ActionToTriggerTheNavigationManager();
160+
161+
// This helper method retrieves the InteractiveRequestOptions object
162+
var requestOptions = navigationManager.History.Last().StateFromJson<InteractiveRequestOptions>();
163+
Asser.NotNull(requestOptions);
164+
Assert.Equal(requestOptions.Interaction, InteractionType.SignIn);
165+
options.TryGetAdditionalParameter("prompt", out string prompt);
166+
Assert.Equal(prompt, "login");
139167
```

src/Directory.Build.props

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

4444
<ItemGroup>
4545
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
46-
<PackageReference Include="Nerdbank.GitVersioning" Version="3.5.109" PrivateAssets="All" />
46+
<PackageReference Include="Nerdbank.GitVersioning" Version="3.5.113" PrivateAssets="All" />
4747

4848
</ItemGroup>
4949

src/bunit.core/ComponentParameterCollectionBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public ComponentParameterCollectionBuilder<TComponent> Add<TChildComponent>(Expr
7272
/// <param name="parameterSelector">A lambda function that selects the parameter.</param>
7373
/// <param name="markup">The markup string to pass to the <see cref="RenderFragment"/>.</param>
7474
/// <returns>This <see cref="ComponentParameterCollectionBuilder{TComponent}"/>.</returns>
75-
public ComponentParameterCollectionBuilder<TComponent> Add(Expression<Func<TComponent, RenderFragment?>> parameterSelector, string markup)
75+
public ComponentParameterCollectionBuilder<TComponent> Add(Expression<Func<TComponent, RenderFragment?>> parameterSelector, [StringSyntax("Html")]string markup)
7676
=> Add(parameterSelector, markup.ToMarkupRenderFragment());
7777

7878
/// <summary>
@@ -260,7 +260,7 @@ public ComponentParameterCollectionBuilder<TComponent> AddChildContent(RenderFra
260260
/// </summary>
261261
/// <param name="markup">The markup string to pass the ChildContent parameter wrapped in a <see cref="RenderFragment"/>.</param>
262262
/// <returns>This <see cref="ComponentParameterCollectionBuilder{TComponent}"/>.</returns>
263-
public ComponentParameterCollectionBuilder<TComponent> AddChildContent(string markup)
263+
public ComponentParameterCollectionBuilder<TComponent> AddChildContent([StringSyntax("Html")]string markup)
264264
=> AddChildContent(markup.ToMarkupRenderFragment());
265265

266266
/// <summary>

src/bunit.core/ComponentParameterFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public static ComponentParameter CascadingValue(object value)
137137
/// </summary>
138138
/// <param name="markup">Markup to pass to the child content parameter.</param>
139139
/// <returns>The <see cref="ComponentParameter"/>.</returns>
140-
public static ComponentParameter ChildContent(string markup)
140+
public static ComponentParameter ChildContent([StringSyntax("Html")]string markup)
141141
{
142142
return RenderFragment(nameof(ChildContent), markup);
143143
}
@@ -173,7 +173,7 @@ public static ComponentParameter ChildContent(RenderFragment renderFragment)
173173
/// <param name="name">Parameter name.</param>
174174
/// <param name="markup">Markup to pass to the render fragment parameter.</param>
175175
/// <returns>The <see cref="ComponentParameter"/>.</returns>
176-
public static ComponentParameter RenderFragment(string name, string markup)
176+
public static ComponentParameter RenderFragment(string name, [StringSyntax("Html")]string markup)
177177
{
178178
return ComponentParameter.CreateParameter(name, markup.ToMarkupRenderFragment());
179179
}

src/bunit.core/Extensions/BlazorExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public static class BlazorExtensions
1010
/// </summary>
1111
/// <param name="markup">Markup to render.</param>
1212
/// <returns>The <see cref="RenderFragment"/>.</returns>
13-
public static RenderFragment ToMarkupRenderFragment(this string? markup)
13+
public static RenderFragment ToMarkupRenderFragment([StringSyntax("Html")]this string? markup)
1414
{
1515
if (string.IsNullOrEmpty(markup))
1616
return _ => { };

src/bunit.core/Extensions/WaitForHelpers/WaitForHelper.cs

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Bunit.Extensions.WaitForHelpers;
99
/// </summary>
1010
public abstract class WaitForHelper<T> : IDisposable
1111
{
12+
private readonly Timer timer;
1213
private readonly TaskCompletionSource<T> checkPassedCompletionSource;
1314
private readonly Func<(bool CheckPassed, T Content)> completeChecker;
1415
private readonly IRenderedFragmentBase renderedFragment;
@@ -51,7 +52,13 @@ protected WaitForHelper(
5152

5253
logger = renderedFragment.Services.CreateLogger<WaitForHelper<T>>();
5354
checkPassedCompletionSource = new TaskCompletionSource<T>();
54-
WaitTask = CreateWaitTask(renderedFragment, timeout);
55+
timer = new Timer(_ =>
56+
{
57+
logger.LogWaiterTimedOut(renderedFragment.ComponentId);
58+
checkPassedCompletionSource.TrySetException(new WaitForFailedException(TimeoutErrorMessage, capturedException));
59+
});
60+
WaitTask = CreateWaitTask(renderedFragment);
61+
timer.Change(GetRuntimeTimeout(timeout), Timeout.InfiniteTimeSpan);
5562

5663
InitializeWaiting();
5764
}
@@ -80,6 +87,7 @@ protected virtual void Dispose(bool disposing)
8087
return;
8188

8289
isDisposed = true;
90+
timer.Dispose();
8391
checkPassedCompletionSource.TrySetCanceled();
8492
renderedFragment.OnAfterRender -= OnAfterRender;
8593
logger.LogWaiterDisposed(renderedFragment.ComponentId);
@@ -105,41 +113,30 @@ private void InitializeWaiting()
105113
}
106114
}
107115

108-
private Task<T> CreateWaitTask(IRenderedFragmentBase renderedFragment, TimeSpan? timeout)
116+
private Task<T> CreateWaitTask(IRenderedFragmentBase renderedFragment)
109117
{
110-
var renderer = renderedFragment.Services.GetRequiredService<ITestRenderer>();
118+
var renderer = renderedFragment
119+
.Services
120+
.GetRequiredService<ITestRenderer>();
111121

112122
// Two to failure conditions, that the renderer captures an unhandled
113123
// exception from a component or itself, or that the timeout is reached,
114124
// are executed on the renderes scheduler, to ensure that OnAfterRender
115125
// and the continuations does not happen at the same time.
116126
var failureTask = renderer.Dispatcher.InvokeAsync(() =>
117127
{
118-
var taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
119-
120-
var renderException = renderer
128+
return renderer
121129
.UnhandledException
122130
.ContinueWith(
123131
x => Task.FromException<T>(x.Result),
124132
CancellationToken.None,
125133
TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously,
126-
taskScheduler);
127-
128-
var timeoutTask = Task.Delay(GetRuntimeTimeout(timeout))
129-
.ContinueWith(
130-
x =>
131-
{
132-
logger.LogWaiterTimedOut(renderedFragment.ComponentId);
133-
return Task.FromException<T>(new WaitForFailedException(TimeoutErrorMessage, capturedException));
134-
},
135-
CancellationToken.None,
136-
TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously,
137-
taskScheduler);
138-
139-
return Task.WhenAny(renderException, timeoutTask).Unwrap();
134+
TaskScheduler.FromCurrentSynchronizationContext());
140135
}).Unwrap();
141136

142-
return Task.WhenAny(failureTask, checkPassedCompletionSource.Task).Unwrap();
137+
return Task
138+
.WhenAny(checkPassedCompletionSource.Task, failureTask)
139+
.Unwrap();
143140
}
144141

145142
private void OnAfterRender(object? sender, EventArgs args)
@@ -170,7 +167,8 @@ private void OnAfterRender(object? sender, EventArgs args)
170167

171168
if (StopWaitingOnCheckException)
172169
{
173-
checkPassedCompletionSource.TrySetException(new WaitForFailedException(CheckThrowErrorMessage, capturedException));
170+
checkPassedCompletionSource.TrySetException(
171+
new WaitForFailedException(CheckThrowErrorMessage, capturedException));
174172
Dispose();
175173
}
176174
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#if !NET7_0_OR_GREATER
2+
namespace System.Diagnostics.CodeAnalysis;
3+
4+
/// <summary>Fake version of the StringSyntaxAttribute, which was introduced in .NET 7</summary>
5+
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
6+
public sealed class StringSyntaxAttribute : Attribute
7+
{
8+
/// <summary>
9+
/// Initializes the <see cref="StringSyntaxAttribute"/> with the identifier of the syntax used.
10+
/// </summary>
11+
public StringSyntaxAttribute(string syntax)
12+
{
13+
}
14+
15+
/// <summary>
16+
/// Initializes the <see cref="StringSyntaxAttribute"/> with the identifier of the syntax used.
17+
/// </summary>
18+
public StringSyntaxAttribute(string syntax, params object?[] arguments)
19+
{
20+
}
21+
22+
/// <summary>The syntax identifier for strings containing URIs.</summary>
23+
public const string Uri = nameof(Uri);
24+
}
25+
#endif

0 commit comments

Comments
 (0)