From 6e16a75b3dba80a75d810610fa148a47b517c801 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Wed, 27 Nov 2024 15:59:35 +0100 Subject: [PATCH] docs: Added docs and exposes via TestContext --- .vscode/tasks.json | 4 +- docs/site/docs/interaction/index.md | 1 + docs/site/docs/interaction/render-modes.md | 157 ++++++++++++++++++ docs/site/docs/toc.md | 1 + .../Rendering/MissingRendererInfoException.cs | 5 +- src/bunit.core/TestContextBase.cs | 10 ++ src/bunit.web/TestContextWrapper.cs | 7 + .../Rendering/RenderModeTests.razor | 2 +- 8 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 docs/site/docs/interaction/render-modes.md diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9aebda5c8..458ec69a3 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -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)", diff --git a/docs/site/docs/interaction/index.md b/docs/site/docs/interaction/index.md index 96e06fdde..9967c90ea 100644 --- a/docs/site/docs/interaction/index.md +++ b/docs/site/docs/interaction/index.md @@ -11,3 +11,4 @@ This section covers the various ways to interact with a component under test, e. - **:** This covers how to manually trigger a render cycle for a component under test. - **:** This covers how to await one or more asynchronous changes to the state of a component under test before continuing the test. - **:** This covers how to dispose components and their children. +- **:** This covers the different render modes and their interaction with bUnit. diff --git a/docs/site/docs/interaction/render-modes.md b/docs/site/docs/interaction/render-modes.md new file mode 100644 index 000000000..51838767e --- /dev/null +++ b/docs/site/docs/interaction/render-modes.md @@ -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 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 +
+ + +
+} +else +{ + // The render mode is Interactive Server, WebAssembly, or Auto + + +} +``` + +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(ps => ps + .SetAssignedRenderMode(RenderMode.InteractiveServer)); + + // Assert + cut.MarkupMatches(""" + + + """); +} + +[Fact] +public void StaticRendering() +{ + // Act + var cut = RenderComponent(); + // This is the same behavior as: + // var cut = RenderComponent(ps => ps + // .SetAssignedRenderMode(null)); + + // Assert + cut.MarkupMatches(""" +
+ + +
+ """); +} +``` + +# [Razor test code](#tab/razor) + +```razor +@inherits TestContext +@code { + [Fact] + public void InteractiveServer() + { + // Act + var cut = Render(@); + + // Assert + cut.MarkupMatches(@ + + + ); + } + + [Fact] + public void StaticRendering() + { + // Act + var cut = Render(@); + + // Assert + cut.MarkupMatches(@
+ + +
); + } +} +``` + +*** + +## Setting the `RendererInfo` during testing +To control the `ComponentBase.RendererInfo` property during testing, use the 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) +{ +

Hey I am your assistant

+} +else +{ +

Loading...

+} +``` + +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(); + + // Assert + cut.MarkupMatches("

Loading...

"); +} + +[Fact] +public void SimulatingInteractiveServerRendering() +{ + // Arrange + SetRendererInfo(new RendererInfo(rendererMode: "Server", isInteractive: true)); + + // Act + var cut = RenderComponent(); + + // Assert + cut.MarkupMatches("

Hey I am your assistant

"); +} +``` + +> [!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. \ No newline at end of file diff --git a/docs/site/docs/toc.md b/docs/site/docs/toc.md index 327afde06..8d937b05c 100644 --- a/docs/site/docs/toc.md +++ b/docs/site/docs/toc.md @@ -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) diff --git a/src/bunit.core/Rendering/MissingRendererInfoException.cs b/src/bunit.core/Rendering/MissingRendererInfoException.cs index 147a254fe..9e3e08466 100644 --- a/src/bunit.core/Rendering/MissingRendererInfoException.cs +++ b/src/bunit.core/Rendering/MissingRendererInfoException.cs @@ -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 diff --git a/src/bunit.core/TestContextBase.cs b/src/bunit.core/TestContextBase.cs index 9c2fa1e8a..d9e3edce8 100644 --- a/src/bunit.core/TestContextBase.cs +++ b/src/bunit.core/TestContextBase.cs @@ -118,4 +118,14 @@ public void DisposeComponents() { Renderer.DisposeComponents(); } + +#if NET9_0_OR_GREATER + /// + /// Sets the for the renderer. + /// + public void SetRendererInfo(RendererInfo? rendererInfo) + { + Renderer.SetRendererInfo(rendererInfo); + } +#endif } diff --git a/src/bunit.web/TestContextWrapper.cs b/src/bunit.web/TestContextWrapper.cs index a6a9c57c0..1ddb194d5 100644 --- a/src/bunit.web/TestContextWrapper.cs +++ b/src/bunit.web/TestContextWrapper.cs @@ -83,6 +83,13 @@ public virtual IRenderedFragment Render(RenderFragment renderFragment) /// public virtual void DisposeComponents() => TestContext?.DisposeComponents(); +#if NET9_0_OR_GREATER + /// + /// Sets the for the renderer. + /// + public virtual void SetRendererInfo(RendererInfo? rendererInfo) => TestContext?.SetRendererInfo(rendererInfo); +#endif + /// /// Dummy method required to allow Blazor's compiler to generate /// C# from .razor files. diff --git a/tests/bunit.core.tests/Rendering/RenderModeTests.razor b/tests/bunit.core.tests/Rendering/RenderModeTests.razor index e389650f3..0bded05f6 100644 --- a/tests/bunit.core.tests/Rendering/RenderModeTests.razor +++ b/tests/bunit.core.tests/Rendering/RenderModeTests.razor @@ -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(); cut.MarkupMatches(