Skip to content

Commit

Permalink
Merge pull request #1625 from bUnit-dev/release/v1.37
Browse files Browse the repository at this point in the history
Release of new minor version v1.37
  • Loading branch information
egil authored Dec 13, 2024
2 parents 653f575 + 5a2c0f4 commit 1bb0b01
Show file tree
Hide file tree
Showing 28 changed files with 667 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"rollForward": false
},
"docfx": {
"version": "2.77.0",
"version": "2.78.2",
"commands": [
"docfx"
],
Expand Down
4 changes: 2 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
{
"label": "Serve Docs (Without Build)",
"type": "shell",
"command": "docfx metadata docs/site/docfx.json && docfx docs/site/docfx.json --serve"
"command": "dotnet docfx metadata docs/site/docfx.json && dotnet docfx docs/site/docfx.json --serve"
},
{
"label": "Serve Docs (With Build for API Documentation)",
"type": "shell",
"command": "dotnet build -c Release && docfx metadata docs/site/docfx.json && docfx docs/site/docfx.json --serve"
"command": "dotnet build -c Release && dotnet docfx metadata docs/site/docfx.json && docfx docs/site/docfx.json --serve"
},
{
"label": "Run all tests (Release Mode)",
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ All notable changes to **bUnit** will be documented in this file. The project ad

## [Unreleased]

### Added
- Added support for `RendererInfo` and `AssignedRenderMode` (`.net9.0`).

## [1.36.0] - 2024-11-12

### Added
Expand Down
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>CA1014,NU5104,NETSDK1138,SYSLIB0051</NoWarn>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>

<!-- Used by code coverage -->
<DebugType>full</DebugType>
Expand Down
46 changes: 23 additions & 23 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<GlobalPackageReference Include="AsyncFixer" Version="1.6.0" PrivateAssets="All" IncludeAssets="Runtime;Build;Native;contentFiles;Analyzers"/>
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167" PrivateAssets="All" IncludeAssets="Runtime;Build;Native;contentFiles;Analyzers"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Meziantou.Polyfill" Version="1.0.40" />
<PackageVersion Include="Meziantou.Polyfill" Version="1.0.42" />
</ItemGroup>

<ItemGroup Label="Shared">
Expand All @@ -28,7 +28,7 @@

<ItemGroup Label="System.Text.Json Vulnerability">
<!-- Due to a CVE in System.Text.Json we explicitly reference the latest version of System.Text.Json -->
<PackageVersion Include="System.Text.Json" Version="8.0.5"/>
<PackageVersion Include="System.Text.Json" Version="9.0.0"/>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
Expand Down Expand Up @@ -58,16 +58,16 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0"/>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4"/>
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="6.0.33"/>

<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.33"/>
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="6.0.33"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.2"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="6.0.33"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.33"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.33"/>
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="6.0.36"/>

<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.36"/>
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="6.0.36"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.3"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="6.0.36"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.36"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.36"/>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
Expand All @@ -86,14 +86,14 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2"/>
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="8.0.10"/>
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="8.0.11"/>

<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.10"/>
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.10"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="8.0.10"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.10"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="8.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.11"/>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
Expand All @@ -103,7 +103,7 @@

<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.0"/>
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.0"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0-rc.2.24473.5"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.0"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.0"/>
Expand All @@ -122,7 +122,7 @@
<PackageVersion Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Shouldly" Version="4.2.1"/>
<PackageVersion Include="Verify.SourceGenerators" Version="2.5.0"/>
<PackageVersion Include="Verify.Xunit" Version="28.1.0"/>
<PackageVersion Include="Verify.Xunit" Version="28.4.0"/>
<PackageVersion Include="Xunit.Combinatorial" Version="1.6.24"/>
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
Expand All @@ -140,10 +140,10 @@

<ItemGroup Label="Source Code Generators">
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.11.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.11.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.12.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.12.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions docs/site/docs/interaction/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ This section covers the various ways to interact with a component under test, e.
- **<xref:trigger-renders>:** This covers how to manually trigger a render cycle for a component under test.
- **<xref:awaiting-async-state>:** This covers how to await one or more asynchronous changes to the state of a component under test before continuing the test.
- **<xref:dispose-components>:** This covers how to dispose components and their children.
- **<xref:render-modes>:** This covers the different render modes and their interaction with bUnit.
157 changes: 157 additions & 0 deletions docs/site/docs/interaction/render-modes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
uid: render-modes
title: Render modes and RendererInfo
---

# Support for render modes and `RendererInfo`
This article explains how to emulate different render modes and `RendererInfo` in bUnit tests.

Render modes in Blazor Web Apps determine the hosting model and interactivity of components. A render mode can be applied to a component using the `@rendermode` directive. The [`RendererInfo`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.rendererinfo?view=aspnetcore-9.0) allows the application to determine the interactivity and location of the component. For more details, see the [Blazor render modes](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-9.0) documentation.

## Setting the render mode for a component under test
Setting the render mode can be done via the <xref:Bunit.ComponentParameterCollectionBuilder`1.SetAssignedRenderMode(Microsoft.AspNetCore.Components.IComponentRenderMode)> method when writing in a C# file. In a razor file use the `@rendermode` directive. Both take an [`IComponentRenderMode`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.icomponentrendermode?view=aspnetcore-9.0) object as a parameter. Normally this is one of the following types:
* [`InteractiveAutoRenderMode`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.interactiveautorendermode?view=aspnetcore-9.0)
* [`InteractiveServerRendeMode`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.interactiveserverrendermode?view=aspnetcore-9.0)
* [`InteractiveWebAssemblyRenderMode`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.interactivewebassemblyrendermode?view=aspnetcore-9.0)

For ease of use the [`RenderMode`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.rendermode?view=aspnetcore-9.0) class defines all three of them.

For example `MovieComponent.razor`:
```razor
@if (AssignedRenderMode is null)
{
// The render mode is Static Server
<form action="/movies">
<input type="text" name="titleFilter" />
<input type="submit" value="Search" />
</form>
}
else
{
// The render mode is Interactive Server, WebAssembly, or Auto
<input @bind="titleFilter" />
<button @onclick="FilterMovies">Search</button>
}
```

The following example shows how to test the above component to check both render modes:

# [C# test code](#tab/csharp)

```csharp
[Fact]
public void InteractiveServer()
{
// Act
var cut = RenderComponent<MovieComponent>(ps => ps
.SetAssignedRenderMode(RenderMode.InteractiveServer));

// Assert
cut.MarkupMatches("""
<input diff:ignoreAttributes />
<button>Search</button>
""");
}

[Fact]
public void StaticRendering()
{
// Act
var cut = RenderComponent<MovieComponent>();
// This is the same behavior as:
// var cut = RenderComponent<MovieComponent>(ps => ps
// .SetAssignedRenderMode(null));
// Assert
cut.MarkupMatches("""
<form action="/movies">
<input type="text" name="titleFilter" />
<input type="submit" value="Search" />
</form>
""");
}
```

# [Razor test code](#tab/razor)

```razor
@inherits TestContext
@code {
[Fact]
public void InteractiveServer()
{
// Act
var cut = Render(@<MovieComponent @rendermode="RenderMode.InteractiveServer" />);
// Assert
cut.MarkupMatches(@<text>
<input diff:ignoreAttributes />
<button>Search</button>
</text>);
}
[Fact]
public void StaticRendering()
{
// Act
var cut = Render(@<MovieComponent />);
// Assert
cut.MarkupMatches(@<form action="/movies">
<input type="text" name="titleFilter" />
<input type="submit" value="Search" />
</form>);
}
}
```

***

## Setting the `RendererInfo` during testing
To control the `ComponentBase.RendererInfo` property during testing, use the <xref:Bunit.TestContextBase.SetRendererInfo(Microsoft.AspNetCore.Components.RendererInfo)> method on the `TestContext` class. The `SetRendererInfo` method takes an nullable `RendererInfo` object as a parameter. Passing `null` will set the `ComponentBase.RendererInfo` to `null`.

A component (`AssistentComponent.razor`) might check if interactivity is given to enable a button:

```razor
@if (RendererInfo.IsInteractive)
{
<p>Hey I am your assistant</p>
}
else
{
<p>Loading...</p>
}
```

In the test, you can set the `RendererInfo` to enable or disable the button:

```csharp
[Fact]
public void SimulatingPreRenderingOnBlazorServer()
{
// Arrange
SetRendererInfo(new RendererInfo(rendererName: "Static", isInteractive: false));

// Act
var cut = RenderComponent<AssistentComponent>();

// Assert
cut.MarkupMatches("<p>Loading...</p>");
}

[Fact]
public void SimulatingInteractiveServerRendering()
{
// Arrange
SetRendererInfo(new RendererInfo(rendererMode: "Server", isInteractive: true));

// Act
var cut = RenderComponent<AssistentComponent>();

// Assert
cut.MarkupMatches("<p>Hey I am your assistant</p>");
}
```

> [!NOTE]
> If a component under test uses the `ComponentBase.RendererInfo` property and the `SetRendererInfo` on `TestContext` hasn't been passed in a `RendererInfo` object, the renderer will throw an exception.
1 change: 1 addition & 0 deletions docs/site/docs/toc.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
## [Trigger renders](xref:trigger-renders)
## [Awaiting an async state change](xref:awaiting-async-state)
## [Disposing components](xref:dispose-components)
## [Render modes and RendererInfo](xref:render-modes)

# [Verifying output](xref:verification)
## [Verify markup](xref:verify-markup)
Expand Down
11 changes: 11 additions & 0 deletions src/bunit.core/ComponentParameterCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ public class ComponentParameterCollection : ICollection<ComponentParameter>, IRe
/// <inheritdoc />
public bool IsReadOnly { get; }

#if NET9_0_OR_GREATER
/// <summary>
/// Gets or sets the <see cref="IComponentRenderMode"/> that will be specified in
/// the render tree for component the parameters are being passed to.
/// </summary>
public IComponentRenderMode? RenderMode { get; set; }
#endif

/// <summary>
/// Adds a <paramref name="item"/> to the collection.
/// </summary>
Expand Down Expand Up @@ -104,6 +112,9 @@ void AddComponent(RenderTreeBuilder builder)
{
builder.OpenComponent<TComponent>(0);
AddAttributes(builder);
#if NET9_0_OR_GREATER
builder.AddComponentRenderMode(RenderMode);
#endif
builder.CloseComponent();
}

Expand Down
Loading

0 comments on commit 1bb0b01

Please sign in to comment.