Skip to content

Commit

Permalink
docs: Added docs and exposes via TestContext
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdotnet committed Dec 13, 2024
1 parent 0e2dbc1 commit 6e16a75
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 4 deletions.
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
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 == true)
{
<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
5 changes: 4 additions & 1 deletion src/bunit.core/Rendering/MissingRendererInfoException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ public class SomeTestClass : TestContext
[Fact]
public void SomeTestCase()
{
Renderer.SetRendererInfo(new RendererInfo("Server", true));
SetRendererInfo(new RendererInfo("Server", true));
...
}
}
The four built in render names are 'Static', 'Server', 'WebAssembly', and 'WebView'.
Go to https://bunit.dev/docs/interaction/render-modes for more information.
""")
{
HelpLink = "https://bunit.dev/docs/interaction/render-modes";
}
}
#endif
10 changes: 10 additions & 0 deletions src/bunit.core/TestContextBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,14 @@ public void DisposeComponents()
{
Renderer.DisposeComponents();
}

#if NET9_0_OR_GREATER
/// <summary>
/// Sets the <see cref="RendererInfo"/> for the renderer.
/// </summary>
public void SetRendererInfo(RendererInfo? rendererInfo)
{
Renderer.SetRendererInfo(rendererInfo);
}
#endif
}
7 changes: 7 additions & 0 deletions src/bunit.web/TestContextWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ public virtual IRenderedFragment Render(RenderFragment renderFragment)
/// </summary>
public virtual void DisposeComponents() => TestContext?.DisposeComponents();

#if NET9_0_OR_GREATER
/// <summary>
/// Sets the <see cref="RendererInfo"/> for the renderer.
/// </summary>
public virtual void SetRendererInfo(RendererInfo? rendererInfo) => TestContext?.SetRendererInfo(rendererInfo);
#endif

/// <summary>
/// Dummy method required to allow Blazor's compiler to generate
/// C# from .razor files.
Expand Down
2 changes: 1 addition & 1 deletion tests/bunit.core.tests/Rendering/RenderModeTests.razor
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[Fact(DisplayName = "TestRenderer provides RendererInfo")]
public void Test001()
{
Renderer.SetRendererInfo(new RendererInfo("Server", true));
SetRendererInfo(new RendererInfo("Server", true));
var cut = RenderComponent<RendererInfoComponent>();

cut.MarkupMatches(
Expand Down

0 comments on commit 6e16a75

Please sign in to comment.