diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 10bd8b7a3..294e26e0b 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -10,7 +10,7 @@ "rollForward": false }, "docfx": { - "version": "2.77.0", + "version": "2.78.2", "commands": [ "docfx" ], diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e106dd43..1b5c93a45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,10 +44,6 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 3.1.x - 5.0.x - 6.0.x - 7.0.x 8.0.x 9.0.x @@ -78,7 +74,6 @@ jobs: dotnet pack src/bunit.core/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.web/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.template/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true - dotnet pack src/bunit.generators/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.web.query/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true # Publish the NuGet package as an artifact, so they can be used in the following jobs @@ -118,6 +113,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-13, windows-latest] + framework: [net8.0, net9.0] runs-on: ${{ matrix.os }} steps: @@ -130,10 +126,6 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 3.1.x - 5.0.x - 6.0.x - 7.0.x 8.0.x 9.0.x @@ -143,7 +135,7 @@ jobs: dotnet tool restore - name: ๐Ÿงช Run unit tests - run: dotnet test -c release --no-restore + run: dotnet test -c release --no-restore -f ${{ matrix.framework }} - name: ๐Ÿ“› Upload hang- and crash-dumps on test failure if: success() || failure() @@ -191,6 +183,14 @@ jobs: dotnet restore ${{ github.workspace }}/TemplateTestXunit --source https://api.nuget.org/v3/index.json --source ${{ env.NUGET_DIRECTORY }} dotnet test ${{ github.workspace }}/TemplateTestXunit + - name: โœ” Verify xUnit.v3 template + run: | + dotnet new bunit --framework xunitv3 --no-restore -o ${{ github.workspace }}/TemplateTestXunitv3 + echo '' >> ${{ github.workspace }}/TemplateTestXunitv3/Directory.Build.props + echo 'false' >> ${{ github.workspace }}/TemplateTestXunitv3/Directory.Packages.props + dotnet restore ${{ github.workspace }}/TemplateTestXunitv3 --source https://api.nuget.org/v3/index.json --source ${{ env.NUGET_DIRECTORY }} + dotnet test ${{ github.workspace }}/TemplateTestXunitv3 + - name: โœ” Verify NUnit template run: | dotnet new bunit --framework nunit --no-restore -o ${{ github.workspace }}/TemplateTestNunit diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 93cdde359..1bf98486a 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -73,10 +73,6 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 3.1.x - 5.0.x - 6.0.x - 7.0.x 8.0.x 9.0.x diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9447b5192..c4b9027e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,10 +58,6 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 3.1.x - 5.0.x - 6.0.x - 7.0.x 8.0.x 9.0.x @@ -102,7 +98,6 @@ jobs: dotnet pack src/bunit/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.core/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.web/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true - dotnet pack src/bunit.generators/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.web.query/ -c Release --property:PackageOutputPath=${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true - name: ๐Ÿ› ๏ธ Upload library to NuGet.org repository 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/CHANGELOG.md b/CHANGELOG.md index 1e9ab3753..eb5271c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,32 @@ All notable changes to **bUnit** will be documented in this file. The project ad ## [Unreleased] +## [1.40.0] - 2025-06-14 + +### Fixed + +- Aligned Microsoft packages to their TFM version + +## [1.39.5] - 2025-04-04 + +### Fixed + +- Do not set the `Uri` or `BaseUri` property on the `FakeNavigationManager` if navigation is prevented by a handler on `net7.0` or greater. Reported and fixed by [@ayyron-dev](https://github.com/ayyron-dev) in [#1647](https://github.com/bUnit-dev/bUnit/issues/1647) +- Use default renderer properties for AngleSharp. Reported by [@jtleaming](https://github.com/jtleaming) in [#1692]. +- `FindComponents` throws an exception, when a base and derived class was searched for. Reported by [@BlueDragon709](https://github.com/BlueDragon709) in [#1691]. + +## [1.38.5] - 2025-01-12 + +### Added + +- Added support for xunit v3 in the bunit.template. By [@linkdotnet](https://github.com/linkdotnet). + +## [1.37.7] - 2024-12-13 + +### Added + +- Added support for `RendererInfo` and `AssignedRenderMode` (`.net9.0`). + ## [1.36.0] - 2024-11-12 ### Added @@ -1423,7 +1449,11 @@ The latest version of the library is availble on NuGet: - **Wrong casing on keyboard event dispatch helpers.** The helper methods for the keyboard events was not probably cased, so that has been updated. E.g. from `Keypress(...)` to `KeyPress(...)`. -[unreleased]: https://github.com/bUnit-dev/bUnit/compare/v1.36.0...HEAD +[unreleased]: https://github.com/bUnit-dev/bUnit/compare/v1.40.0...HEAD +[1.40.0]: https://github.com/bUnit-dev/bUnit/compare/v1.39.5...v1.40.0 +[1.39.5]: https://github.com/bUnit-dev/bUnit/compare/v1.38.5...1.39.5 +[1.38.5]: https://github.com/bUnit-dev/bUnit/compare/v1.37.7...v1.38.5 +[1.37.7]: https://github.com/bUnit-dev/bUnit/compare/v1.36.0...1.37.7 [1.36.0]: https://github.com/bUnit-dev/bUnit/compare/v1.35.3...v1.36.0 [1.35.3]: https://github.com/bUnit-dev/bUnit/compare/v1.34.0...1.35.3 [1.34.0]: https://github.com/bUnit-dev/bUnit/compare/v1.33.3...v1.34.0 diff --git a/Directory.Build.props b/Directory.Build.props index a3c2a053a..b2194a03c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,6 +16,7 @@ enable CA1014,NU5104,NETSDK1138,SYSLIB0051 false + true full diff --git a/Directory.Packages.props b/Directory.Packages.props index c25fef9bc..63b989f88 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,132 +6,128 @@ - - - + + + - + - + - - + + - - - - - - + - + - + - + - - - - - - - - + + + + + + + + - + - + - - + + - - - + + + + - - - - - - - - - - + + + + + + + + + + - - - + + + - + - - + + - + - - - - + + + + - - - + + + @@ -140,10 +136,10 @@ - - - - + + + + diff --git a/README.md b/README.md index a923f6597..2d9170d9f 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,6 @@ # bUnit - a testing library for Blazor components - - - - - - - **bUnit** is a testing library for Blazor Components. Its goal is to make it easy to write _comprehensive, stable_ unit tests. With bUnit, you can: - Setup and define components under tests using C# or Razor syntax @@ -40,22 +33,22 @@ To get started, head to the [getting started documentation](https://bunit.dev/do ## Sponsors -A huge thank you to the [sponsors of my work with bUnit](https://github.com/sponsors/egil). The higher tier sponsors are: +A huge thank you to the [sponsors of bUnit](https://github.com/sponsors/egil). The higher tier sponsors are: diff --git a/bunit.sln b/bunit.sln index 07a425cf2..2a6f50753 100644 --- a/bunit.sln +++ b/bunit.sln @@ -64,10 +64,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.web.query.tests", "te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.generators.internal", "src\bunit.generators.internal\bunit.generators.internal.csproj", "{AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.generators.tests", "tests\bunit.generators.tests\bunit.generators.tests.csproj", "{09046981-D9EC-4295-8502-721AC54E1F12}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.generators", "src\bunit.generators\bunit.generators.csproj", "{A7C6A2AA-FF8F-4ED1-8590-5324FC566059}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -110,14 +106,6 @@ Global {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|Any CPU.Build.0 = Debug|Any CPU {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|Any CPU.ActiveCfg = Release|Any CPU {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|Any CPU.Build.0 = Release|Any CPU - {09046981-D9EC-4295-8502-721AC54E1F12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {09046981-D9EC-4295-8502-721AC54E1F12}.Debug|Any CPU.Build.0 = Debug|Any CPU - {09046981-D9EC-4295-8502-721AC54E1F12}.Release|Any CPU.ActiveCfg = Release|Any CPU - {09046981-D9EC-4295-8502-721AC54E1F12}.Release|Any CPU.Build.0 = Release|Any CPU - {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -133,8 +121,6 @@ Global {0FF92169-7D8F-46A2-8327-A2F028CB426F} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59} {DE975A0C-0672-4248-913E-D267C1001801} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520} {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59} - {09046981-D9EC-4295-8502-721AC54E1F12} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520} - {A7C6A2AA-FF8F-4ED1-8590-5324FC566059} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {24106918-1C86-4769-BDA6-9C80E64CD260} 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..fc40eee6d --- /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) +{ +

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/docs/site/docs/verification/semantic-html-comparison.md b/docs/site/docs/verification/semantic-html-comparison.md index 0ef9bd5d0..f03fa02f3 100644 --- a/docs/site/docs/verification/semantic-html-comparison.md +++ b/docs/site/docs/verification/semantic-html-comparison.md @@ -105,7 +105,7 @@ Here are the customization options you have available to you: ```html

HeLLo world

-
``` To perform case insensitive comparison of the text inside the `id` attribute, do the following: diff --git a/docs/site/index.md b/docs/site/index.md index aa7eef8bf..c10804d1e 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -50,19 +50,19 @@ bUnit is available on NuGet in various incarnations. Most users should just pick ## Sponsors -A huge thank you to the [sponsors of my work with bUnit](https://github.com/sponsors/egil). The higher tier sponsors are: +A huge thank you to the [sponsors of bUnit](https://github.com/sponsors/egil). The higher tier sponsors are: ## Contributors diff --git a/docs/site/templates/bunit/layout/_master.tmpl b/docs/site/templates/bunit/layout/_master.tmpl index 8446a5ad0..c046a765d 100644 --- a/docs/site/templates/bunit/layout/_master.tmpl +++ b/docs/site/templates/bunit/layout/_master.tmpl @@ -138,18 +138,6 @@
-
- - Progress Telerik - -

Premium sponsor: Progress Telerik.

-
-
- - Packt - -

Editorial support provided by Packt.

-
.NET Foundation diff --git a/src/bunit.core/Asserting/ActualExpectedAssertException.cs b/src/bunit.core/Asserting/ActualExpectedAssertException.cs index 4a75fdc18..498d302eb 100644 --- a/src/bunit.core/Asserting/ActualExpectedAssertException.cs +++ b/src/bunit.core/Asserting/ActualExpectedAssertException.cs @@ -1,10 +1,12 @@ +using Bunit.Internal.XUnitExceptions; + namespace Bunit.Asserting; /// /// Represents a generic assert exception used when an actual result does not match an expected result. /// [Serializable] -public class ActualExpectedAssertException : Exception +public class ActualExpectedAssertException : Exception, IAssertionException { /// /// Initializes a new instance of the class. diff --git a/src/bunit.core/ComponentParameterCollection.cs b/src/bunit.core/ComponentParameterCollection.cs index 7b248b096..9f179bc94 100644 --- a/src/bunit.core/ComponentParameterCollection.cs +++ b/src/bunit.core/ComponentParameterCollection.cs @@ -19,6 +19,14 @@ public class ComponentParameterCollection : ICollection, IRe /// public bool IsReadOnly { get; } +#if NET9_0_OR_GREATER + /// + /// Gets or sets the that will be specified in + /// the render tree for component the parameters are being passed to. + /// + public IComponentRenderMode? RenderMode { get; set; } +#endif + /// /// Adds a to the collection. /// @@ -104,6 +112,9 @@ void AddComponent(RenderTreeBuilder builder) { builder.OpenComponent(0); AddAttributes(builder); +#if NET9_0_OR_GREATER + builder.AddComponentRenderMode(RenderMode); +#endif builder.CloseComponent(); } diff --git a/src/bunit.core/ComponentParameterCollectionBuilder.cs b/src/bunit.core/ComponentParameterCollectionBuilder.cs index f4c1fa04e..492b035a5 100644 --- a/src/bunit.core/ComponentParameterCollectionBuilder.cs +++ b/src/bunit.core/ComponentParameterCollectionBuilder.cs @@ -78,7 +78,7 @@ public ComponentParameterCollectionBuilder Add(Expr /// A lambda function that selects the parameter. /// The markup string to pass to the . /// This . - public ComponentParameterCollectionBuilder Add(Expression> parameterSelector, [StringSyntax("Html")]string markup) + public ComponentParameterCollectionBuilder Add(Expression> parameterSelector, [StringSyntax("Html")] string markup) => Add(parameterSelector, markup.ToMarkupRenderFragment()); /// @@ -266,7 +266,7 @@ public ComponentParameterCollectionBuilder AddChildContent(RenderFra /// /// The markup string to pass the ChildContent parameter wrapped in a . /// This . - public ComponentParameterCollectionBuilder AddChildContent([StringSyntax("Html")]string markup) + public ComponentParameterCollectionBuilder AddChildContent([StringSyntax("Html")] string markup) => AddChildContent(markup.ToMarkupRenderFragment()); /// @@ -344,11 +344,11 @@ public ComponentParameterCollectionBuilder Bind( Action changedAction, Expression>? valueExpression = null) { - #if !NET8_0_OR_GREATER +#if !NET8_0_OR_GREATER var (parameterName, _, isCascading) = GetParameterInfo(parameterSelector); - #else +#else var (parameterName, _, isCascading) = GetParameterInfo(parameterSelector, initialValue); - #endif +#endif if (isCascading) throw new ArgumentException("Using Bind with a cascading parameter is not allowed.", parameterName); @@ -397,6 +397,19 @@ static string TrimEnd(string source, string value) : source; } +#if NET9_0_OR_GREATER + /// + /// Sets (or unsets) the for the component and child components. + /// + /// The render mode to assign to the component, e.g. RenderMode.InteractiveServer, or , to not assign a specific render mode. + /// This . + public ComponentParameterCollectionBuilder SetAssignedRenderMode(IComponentRenderMode? renderMode) + { + parameters.RenderMode = renderMode; + return this; + } +#endif + /// /// Try to add a for a parameter with the , if /// has a property with that name, AND that property has a @@ -454,14 +467,14 @@ Expression> parameterSelector : propInfoCandidate; var paramAttr = propertyInfo?.GetCustomAttribute(inherit: true); - #if !NET8_0_OR_GREATER +#if !NET8_0_OR_GREATER var cascadingParamAttr = propertyInfo?.GetCustomAttribute(inherit: true); if (propertyInfo is null || (paramAttr is null && cascadingParamAttr is null)) throw new ArgumentException($"The parameter selector '{parameterSelector}' does not resolve to a public property on the component '{typeof(TComponent)}' with a [Parameter] or [CascadingParameter] attribute.", nameof(parameterSelector)); return (propertyInfo.Name, CascadingValueName: cascadingParamAttr?.Name, IsCascading: cascadingParamAttr is not null); - #else +#else var cascadingParamAttrBase = propertyInfo?.GetCustomAttribute(inherit: true); if (propertyInfo is null || (paramAttr is null && cascadingParamAttrBase is null)) @@ -494,7 +507,7 @@ static ArgumentException CreateErrorMessageForSupplyFromQuery( NavigationManager.NavigateTo(uri); """); } - #endif +#endif } private static bool HasChildContentParameter() diff --git a/src/bunit.core/Extensions/WaitForHelpers/WaitForFailedException.cs b/src/bunit.core/Extensions/WaitForHelpers/WaitForFailedException.cs index 3b9a8fc25..d84d68cb9 100644 --- a/src/bunit.core/Extensions/WaitForHelpers/WaitForFailedException.cs +++ b/src/bunit.core/Extensions/WaitForHelpers/WaitForFailedException.cs @@ -1,10 +1,12 @@ +using Bunit.Internal.XUnitExceptions; + namespace Bunit.Extensions.WaitForHelpers; /// /// Represents an exception thrown when the does not complete successfully. /// [Serializable] -public sealed class WaitForFailedException : Exception +public sealed class WaitForFailedException : Exception, ITestTimeoutException { /// /// Initializes a new instance of the class. diff --git a/src/bunit.core/Internal/XUnitExceptions/IAssertionException.cs b/src/bunit.core/Internal/XUnitExceptions/IAssertionException.cs new file mode 100644 index 000000000..bf8db4fd1 --- /dev/null +++ b/src/bunit.core/Internal/XUnitExceptions/IAssertionException.cs @@ -0,0 +1,6 @@ +namespace Bunit.Internal.XUnitExceptions; + +/// +/// This is a marker interface for xUnit.v3 that will cause xUnit to consider the failure cause to be an assertion failure. +/// +internal interface IAssertionException; diff --git a/src/bunit.core/Internal/XUnitExceptions/ITestTimeoutException.cs b/src/bunit.core/Internal/XUnitExceptions/ITestTimeoutException.cs new file mode 100644 index 000000000..aec0961ea --- /dev/null +++ b/src/bunit.core/Internal/XUnitExceptions/ITestTimeoutException.cs @@ -0,0 +1,6 @@ +namespace Bunit.Internal.XUnitExceptions; + +/// +/// This is a marker interface for xUnit.v3 that will cause xUnit to consider the failure cause to be a timeout. +/// +internal interface ITestTimeoutException; diff --git a/src/bunit.core/ParameterException.cs b/src/bunit.core/ParameterException.cs index 66ab60a14..dd9cee220 100644 --- a/src/bunit.core/ParameterException.cs +++ b/src/bunit.core/ParameterException.cs @@ -1,7 +1,7 @@ namespace Bunit.RazorTesting; /// -/// Represents an missing or invalid Blazor parameter on a Blazor component. +/// Represents a missing or invalid Blazor parameter on a Blazor component. /// [Serializable] public sealed class ParameterException : ArgumentException diff --git a/src/bunit.core/Rendering/ITestRenderer.cs b/src/bunit.core/Rendering/ITestRenderer.cs index 50f8d3e72..cfd855d33 100644 --- a/src/bunit.core/Rendering/ITestRenderer.cs +++ b/src/bunit.core/Rendering/ITestRenderer.cs @@ -78,4 +78,11 @@ IReadOnlyList> FindComponents(IRe /// Disposes all components rendered by the . /// void DisposeComponents(); + +#if NET9_0_OR_GREATER + /// + /// Sets the for the renderer. + /// + void SetRendererInfo(RendererInfo? rendererInfo); +#endif } diff --git a/src/bunit.core/Rendering/MissingRendererInfoException.cs b/src/bunit.core/Rendering/MissingRendererInfoException.cs new file mode 100644 index 000000000..a763a0d98 --- /dev/null +++ b/src/bunit.core/Rendering/MissingRendererInfoException.cs @@ -0,0 +1,36 @@ +#if NET9_0_OR_GREATER +namespace Bunit.Rendering; + +/// +/// Represents an exception that is thrown when a component under test is trying to access the 'RendererInfo' property, which has not been specified. +/// +public sealed class MissingRendererInfoException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public MissingRendererInfoException() + : base(""" + A component under test is trying to access the 'RendererInfo' property, which has not been specified. Set it via TestContext.Renderer.SetRendererInfo. + + For example: + + public class SomeTestClass : TestContext + { + [Fact] + public void SomeTestCase() + { + 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/Rendering/RenderModeMisMatchException.cs b/src/bunit.core/Rendering/RenderModeMisMatchException.cs new file mode 100644 index 000000000..8187988e3 --- /dev/null +++ b/src/bunit.core/Rendering/RenderModeMisMatchException.cs @@ -0,0 +1,22 @@ +#if NET9_0_OR_GREATER +namespace Bunit.Rendering; + +/// +/// Represents an exception that is thrown when a component under test has mismatching render modes assigned between parent and child components. +/// +public sealed class RenderModeMisMatchException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public RenderModeMisMatchException() + : base(""" + A component under test has mismatching render modes assigned between parent and child components. + Ensure that the render mode of the parent component matches the render mode of the child component. + Learn more about render modes at https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-9.0#render-mode-propagation. + """) + { + HelpLink = "https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-9.0#render-mode-propagation"; + } +} +#endif diff --git a/src/bunit.core/Rendering/TestRenderer.cs b/src/bunit.core/Rendering/TestRenderer.cs index 61d4deecf..18625e39e 100644 --- a/src/bunit.core/Rendering/TestRenderer.cs +++ b/src/bunit.core/Rendering/TestRenderer.cs @@ -1,7 +1,7 @@ -using Microsoft.Extensions.Logging; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; +using Microsoft.Extensions.Logging; namespace Bunit.Rendering; @@ -64,6 +64,21 @@ private bool IsBatchInProgress /// internal int RenderCount { get; private set; } +#if NET9_0_OR_GREATER + private RendererInfo? rendererInfo; + + /// + [SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "The exception is raised to guide users.")] + protected override RendererInfo RendererInfo => rendererInfo ?? throw new MissingRendererInfoException(); + + /// + public void SetRendererInfo(RendererInfo? rendererInfo) + { + this.rendererInfo = rendererInfo; + } + +#endif + #if NETSTANDARD /// /// Initializes a new instance of the class. @@ -226,6 +241,64 @@ protected override IComponent ResolveComponentForRenderMode(Type componentType, } #endif +#if NET9_0_OR_GREATER + /// + protected override IComponentRenderMode? GetComponentRenderMode(IComponent component) + { + ArgumentNullException.ThrowIfNull(component); + + // Search from the current component all the way up the render tree. + // All components must have the same render mode specified (or none at all). + // Return the render mode that is found after checking the full tree. + return GetAndValidateRenderMode(component, childRenderMode: null); + + IComponentRenderMode? GetAndValidateRenderMode(IComponent component, IComponentRenderMode? childRenderMode) + { + var componentState = GetComponentState(component); + var renderMode = GetRenderModeForComponent(componentState); + + if (childRenderMode is not null && renderMode is not null && childRenderMode != renderMode) + { + throw new RenderModeMisMatchException(); + } + + return componentState.ParentComponentState is null + ? renderMode ?? childRenderMode + : GetAndValidateRenderMode(componentState.ParentComponentState.Component, renderMode ?? childRenderMode); + } + + IComponentRenderMode? GetRenderModeForComponent(ComponentState componentState) + { + var renderModeAttribute = componentState.Component.GetType().GetCustomAttribute(); + if (renderModeAttribute is { Mode: not null }) + { + return renderModeAttribute.Mode; + } + + if (componentState.ParentComponentState is not null) + { + var parentFrames = GetCurrentRenderTreeFrames(componentState.ParentComponentState.ComponentId); + var foundComponentStart = false; + for (var i = 0; i < parentFrames.Count; i++) + { + ref var frame = ref parentFrames.Array[i]; + + if (frame.FrameType is RenderTreeFrameType.Component) + { + foundComponentStart = frame.ComponentId == componentState.ComponentId; + } + else if (foundComponentStart && frame.FrameType is RenderTreeFrameType.ComponentRenderMode) + { + return frame.ComponentRenderMode; + } + } + } + + return null; + } + } +#endif + /// internal Task SetDirectParametersAsync(IRenderedFragmentBase renderedComponent, ParameterView parameters) { @@ -537,7 +610,13 @@ private IRenderedComponentBase GetOrCreateRenderedComponent)renderedComponent; + if (renderedComponent is IRenderedComponentBase typedComponent) + { + return typedComponent; + } + + renderedComponent.Dispose(); + renderedComponents.Remove(componentId); } LoadRenderTreeFrames(componentId, framesCollection); 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.template/template/.template.config/dotnetcli.host.json b/src/bunit.template/template/.template.config/dotnetcli.host.json index c8adcf8a7..ced81d900 100644 --- a/src/bunit.template/template/.template.config/dotnetcli.host.json +++ b/src/bunit.template/template/.template.config/dotnetcli.host.json @@ -15,6 +15,7 @@ }, "usageExamples": [ "--framework xunit --sdk net8.0", + "--framework xunitv3 --sdk net8.0", "--framework nunit --sdk net8.0", "--framework mstest --sdk net8.0" ] diff --git a/src/bunit.template/template/.template.config/template.json b/src/bunit.template/template/.template.config/template.json index 646e9a8ef..81e3820c6 100644 --- a/src/bunit.template/template/.template.config/template.json +++ b/src/bunit.template/template/.template.config/template.json @@ -24,7 +24,7 @@ "modifiers": [ { "exclude": [ "BunitTestContext.cs" ], - "condition": "(testFramework_xunit)" + "condition": "(testFramework_xunit || testFramework_xunitv3)" } ] } @@ -58,6 +58,11 @@ "description": "xUnit unit testing framework", "displayName": "xUnit" }, + { + "choice": "xunitv3", + "description": "xUnit v3 unit testing framework", + "displayName": "xUnit v3" + }, { "choice": "mstest", "description": "MSTest unit testing framework", @@ -73,6 +78,10 @@ "type": "computed", "value": "UnitTestFramework == \"xunit\"" }, + "testFramework_xunitv3": { + "type": "computed", + "value": "UnitTestFramework == \"xunitv3\"" + }, "testFramework_mstest": { "type": "computed", "value": "UnitTestFramework == \"mstest\"" @@ -85,16 +94,6 @@ "defaultValue": "net9.0", "replaces": "targetSdk", "choices": [ - { - "choice": "net6.0", - "description": ".net 6.0", - "displayName": ".net 6.0" - }, - { - "choice": "net7.0", - "description": ".net 7.0", - "displayName": ".net 7.0" - }, { "choice": "net8.0", "description": ".net 8.0", diff --git a/src/bunit.template/template/Company.BlazorTests1.csproj b/src/bunit.template/template/Company.BlazorTests1.csproj index 35e5fe7d5..fe62ba21c 100644 --- a/src/bunit.template/template/Company.BlazorTests1.csproj +++ b/src/bunit.template/template/Company.BlazorTests1.csproj @@ -11,35 +11,44 @@ + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + all runtime; build; native; contentfiles; analyzers + + + + all + runtime; build; native; contentfiles; analyzers + + + - + - - + + diff --git a/src/bunit.template/template/CounterCSharpTests.cs b/src/bunit.template/template/CounterCSharpTests.cs index 020dfabbd..359fc45c4 100644 --- a/src/bunit.template/template/CounterCSharpTests.cs +++ b/src/bunit.template/template/CounterCSharpTests.cs @@ -6,6 +6,9 @@ namespace Company.BlazorTests1; /// #if (testFramework_xunit) public class CounterCSharpTests : TestContext +#elif (testFramework_xunitv3) +// The full qualified namespace for bUnit TestContext is used here as xunit v3 also offers a TestContext class +public class CounterCSharpTests : Bunit.TestContext #elif (testFramework_nunit) public class CounterCSharpTests : BunitTestContext #elif (testFramework_mstest) @@ -15,6 +18,8 @@ public class CounterCSharpTests : BunitTestContext { #if (testFramework_xunit) [Fact] +#elif (testFramework_xunitv3) + [Fact] #elif (testFramework_nunit) [Test] #elif (testFramework_mstest) @@ -31,6 +36,8 @@ public void CounterStartsAtZero() #if (testFramework_xunit) [Fact] +#elif (testFramework_xunitv3) + [Fact] #elif (testFramework_nunit) [Test] #elif (testFramework_mstest) diff --git a/src/bunit.template/template/CounterRazorTests.razor b/src/bunit.template/template/CounterRazorTests.razor index 7992eda2d..09f4ad12d 100644 --- a/src/bunit.template/template/CounterRazorTests.razor +++ b/src/bunit.template/template/CounterRazorTests.razor @@ -1,5 +1,8 @@ -@*#if (testFramework_xunit)*@ +@*#if (testFramework_xunit) *@ @inherits TestContext +@*#elif (testFramework_xunitv3)*@ +@* The full qualified namespace for bUnit TestContext is used here as xunit v3 also offers a TestContext class *@ +@inherits Bunit.TestContext @*#elif (testFramework_nunit)*@ @inherits BunitTestContext @*#elif (testFramework_mstest)*@ @@ -14,6 +17,8 @@ Learn more at https://bunit.dev/docs/getting-started/writing-tests.html#creating @code { @*#if (testFramework_xunit)*@ [Fact] +@*#elif (testFramework_xunitv3)*@ + [Fact] @*#elif (testFramework_nunit)*@ [Test] @*#elif (testFramework_mstest)*@ @@ -29,6 +34,8 @@ Learn more at https://bunit.dev/docs/getting-started/writing-tests.html#creating } @*#if (testFramework_xunit)*@ [Fact] +@*#elif (testFramework_xunitv3)*@ + [Fact] @*#elif (testFramework_nunit)*@ [Test] @*#elif (testFramework_mstest)*@ diff --git a/src/bunit.template/template/_Imports.razor b/src/bunit.template/template/_Imports.razor index 054402d02..e49ef971e 100644 --- a/src/bunit.template/template/_Imports.razor +++ b/src/bunit.template/template/_Imports.razor @@ -5,6 +5,8 @@ @using Bunit.TestDoubles @*#if (testFramework_xunit)*@ @using Xunit +@*#elif (testFramework_xunitv3)*@ +@using Xunit @*#elif (testFramework_nunit)*@ @using NUnit.Framework @*#elif (testFramework_mstest)*@ diff --git a/src/bunit.web/Diffing/DiffMarkupFormatter.cs b/src/bunit.web/Diffing/DiffMarkupFormatter.cs index ccb033627..4feffd455 100644 --- a/src/bunit.web/Diffing/DiffMarkupFormatter.cs +++ b/src/bunit.web/Diffing/DiffMarkupFormatter.cs @@ -16,6 +16,7 @@ public class DiffMarkupFormatter : PrettyMarkupFormatter, IMarkupFormatter /// The is not thread safe, so using this singleton /// instance to format elements may not result in the desired effect. /// + [Obsolete("This instance is not thread safe, use a new instance instead.")] public static new readonly DiffMarkupFormatter Instance = new(); /// diff --git a/src/bunit.web/Extensions/ElementRemovedFromDomException.cs b/src/bunit.web/Extensions/ElementRemovedFromDomException.cs index 6bc370599..4b0cf2db0 100644 --- a/src/bunit.web/Extensions/ElementRemovedFromDomException.cs +++ b/src/bunit.web/Extensions/ElementRemovedFromDomException.cs @@ -2,7 +2,7 @@ namespace Bunit; /// /// Represents an exception that is thrown when trying to access an element -/// that was previous found in the DOM. +/// that was previously found in the DOM. /// [Serializable] public sealed class ElementRemovedFromDomException : ElementNotFoundException diff --git a/src/bunit.web/Extensions/NodePrintExtensions.cs b/src/bunit.web/Extensions/NodePrintExtensions.cs index c54924824..d113d2642 100644 --- a/src/bunit.web/Extensions/NodePrintExtensions.cs +++ b/src/bunit.web/Extensions/NodePrintExtensions.cs @@ -109,6 +109,7 @@ public static string ToMarkupElementOnly(this IElement element) if (element is null) throw new ArgumentNullException(nameof(element)); + var diffMarkupFormatter = new DiffMarkupFormatter(); var result = new StringBuilder(); result.Append(Symbols.LessThan); @@ -120,7 +121,7 @@ public static string ToMarkupElementOnly(this IElement element) foreach (var attribute in element.Attributes) { - result.Append(' ').Append(DiffMarkupFormatter.Instance.ConvertToString(attribute)); + result.Append(' ').Append(diffMarkupFormatter.ConvertToString(attribute)); } if (element.HasChildNodes) diff --git a/src/bunit.web/Rendering/BunitHtmlParser.cs b/src/bunit.web/Rendering/BunitHtmlParser.cs index 6f8edb5f7..08093cc0a 100644 --- a/src/bunit.web/Rendering/BunitHtmlParser.cs +++ b/src/bunit.web/Rendering/BunitHtmlParser.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Diagnostics; using AngleSharp; +using AngleSharp.Css; using AngleSharp.Dom; using AngleSharp.Html.Parser; using Bunit.Diffing; @@ -28,7 +29,8 @@ public sealed class BunitHtmlParser : IDisposable /// with a AngleSharp context without a registered. /// public BunitHtmlParser() - : this(Configuration.Default.WithCss().With(new HtmlComparer())) { } + : this(Configuration.Default.WithCss() + .With(new HtmlComparer())) { } /// /// Initializes a new instance of the class @@ -43,7 +45,13 @@ public BunitHtmlParser(HtmlComparer htmlComparer, TestContextBase testContext) private BunitHtmlParser(IConfiguration angleSharpConfiguration) { - var config = angleSharpConfiguration.With(this); + var config = angleSharpConfiguration + .With(this) + .WithRenderDevice(new DefaultRenderDevice + { + ViewPortWidth = 1920, + ViewPortHeight = 1080, + }); context = BrowsingContext.New(config); var parseOptions = new HtmlParserOptions { 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/src/bunit.web/TestDoubles/NavigationManager/FakeNavigationManager.cs b/src/bunit.web/TestDoubles/NavigationManager/FakeNavigationManager.cs index 574807cf3..85a27b1b4 100644 --- a/src/bunit.web/TestDoubles/NavigationManager/FakeNavigationManager.cs +++ b/src/bunit.web/TestDoubles/NavigationManager/FakeNavigationManager.cs @@ -75,13 +75,6 @@ protected override void NavigateToCore(string uri, NavigationOptions options) var absoluteUri = GetNewAbsoluteUri(uri); var changedBaseUri = HasDifferentBaseUri(absoluteUri); - if (changedBaseUri) - { - BaseUri = GetBaseUri(absoluteUri); - } - - Uri = ToAbsoluteUri(uri).OriginalString; - if (options.ReplaceHistoryEntry && history.Count > 0) history.Pop(); @@ -92,7 +85,6 @@ protected override void NavigateToCore(string uri, NavigationOptions options) testContextBase.Renderer.Dispatcher.InvokeAsync(() => #endif { - Uri = absoluteUri.OriginalString; #if NET7_0_OR_GREATER var shouldContinueNavigation = false; @@ -116,6 +108,12 @@ protected override void NavigateToCore(string uri, NavigationOptions options) 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 @@ -125,17 +123,13 @@ protected override void NavigateToCore(string uri, NavigationOptions options) { NotifyLocationChanged(isInterceptedLink: false); } - else - { - BaseUri = GetBaseUri(absoluteUri); - } }); } #endif #if NET7_0_OR_GREATER /// - protected override void SetNavigationLockState(bool value) {} + protected override void SetNavigationLockState(bool value) { } /// protected override void HandleLocationChangingHandlerException(Exception ex, LocationChangingContext context) diff --git a/tests/bunit.core.tests/Rendering/RenderModeTests.razor b/tests/bunit.core.tests/Rendering/RenderModeTests.razor new file mode 100644 index 000000000..557b3386e --- /dev/null +++ b/tests/bunit.core.tests/Rendering/RenderModeTests.razor @@ -0,0 +1,176 @@ +@code{ + #if NET9_0_OR_GREATER +} +@using Bunit.TestAssets.RenderModes; +@inherits TestContext +@code { + [Fact(DisplayName = "TestRenderer provides RendererInfo")] + public void Test001() + { + SetRendererInfo(new RendererInfo("Server", true)); + var cut = RenderComponent(); + + cut.MarkupMatches( + @ +

Is interactive: True

+

Rendermode: Server

+
); + } + + [Fact(DisplayName = "Renderer throws exception if RendererInfo is not specified")] + public void Test002() + { + Action act = () => RenderComponent(); + + act.ShouldThrow(); + } + + [Fact(DisplayName = "Renderer should set the RenderModeAttribute on the component")] + public void Test003() + { + var cut = RenderComponent(); + + cut.MarkupMatches(@
Assigned render mode: InteractiveServerRenderMode
); + } + + [Fact(DisplayName = "The AssignedRenderMode is based on the RenderModeAttribute in the component hierarchy where parent component has no RenderMode")] + public void Test004() + { + var cut = Render( + @ + + ); + + cut.MarkupMatches( + @ +
Parent assigned render mode:
+
Assigned render mode: InteractiveWebAssemblyRenderMode
+
); + } + + [Fact(DisplayName = "Parent and child render mode is specified")] + public void Test005() + { + var cut = Render( + @ + + ); + + cut.MarkupMatches( + @ +
Parent assigned render mode: InteractiveServerRenderMode
+
Assigned render mode: InteractiveServerRenderMode
+
); + } + + [Fact(DisplayName = "Parent and child render mode is not specified")] + public void Test006() + { + var cut = Render( + @ + + ); + + cut.MarkupMatches( + @ +
Parent assigned render mode:
+
Assigned render mode:
+
); + } + + [Fact(DisplayName = "Rendermode specified on child")] + public void Test007() + { + var cut = Render( + @ + + ); + + cut.MarkupMatches(@

Assigned Render Mode: InteractiveServerRenderMode

); + } + + [Fact(DisplayName = "Assigned Render Mode is inherited all the way down the component hierarchy")] + public void Test008() + { + var cut = Render( + @ + + + + ); + + cut.MarkupMatches(@

Assigned Render Mode: InteractiveServerRenderMode

); + } + + [Fact(DisplayName = "Having a component with section outlet and RenderMode is specifying for child component")] + public void Test009() + { + // See: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/sections?view=aspnetcore-8.0#section-interaction-with-other-blazor-features + var cut = Render(@); + + cut.MarkupMatches(@

Assigned Render Mode: InteractiveWebAssemblyRenderMode

); + } + + [Fact(DisplayName = "Assigned Render Mode on siblings")] + public void Test010() + { + var cut = Render( + @ + + + ); + + cut.MarkupMatches( + @ +

Assigned Render Mode: InteractiveServerRenderMode

+

Assigned Render Mode: InteractiveWebAssemblyRenderMode

+
); + } + + [Fact(DisplayName = "SetAssignedRenderMode on root component")] + public void Test011() + { + var cut = RenderComponent(ps => ps.SetAssignedRenderMode(RenderMode.InteractiveServer)); + cut.MarkupMatches(@

Assigned Render Mode: InteractiveServerRenderMode

); + } + + [Fact(DisplayName = "SetAssignedRenderMode on parent component cascades to children")] + public void Test012() + { + var cut = RenderComponent(ps => ps + .SetAssignedRenderMode(RenderMode.InteractiveWebAssembly) + .AddChildContent()); + + cut.MarkupMatches(@

Assigned Render Mode: InteractiveWebAssemblyRenderMode

); + } + + [Fact(DisplayName = "SetAssignedRenderMode child components")] + public void Test013() + { + var cut = RenderComponent(ps => ps + .AddChildContent(pps => pps.SetAssignedRenderMode(RenderMode.InteractiveServer)) + .AddChildContent(pps => pps.SetAssignedRenderMode(RenderMode.InteractiveWebAssembly))); + + cut.MarkupMatches( + @ +

Assigned Render Mode: InteractiveServerRenderMode

+

Assigned Render Mode: InteractiveWebAssemblyRenderMode

+
); + } + + [Fact(DisplayName = "Different assigned RenderMode between child and parent throws")] + public void Test020() + { + var act = () => Render( + @ + + + + ); + + act.ShouldThrow(); + } +} +@code{ + #endif +} diff --git a/tests/bunit.core.tests/TestDoubles/PersistentComponentState/FakePersistentComponentStateTest.cs b/tests/bunit.core.tests/TestDoubles/PersistentComponentState/FakePersistentComponentStateTest.cs index cb3d256a2..f54aa345c 100644 --- a/tests/bunit.core.tests/TestDoubles/PersistentComponentState/FakePersistentComponentStateTest.cs +++ b/tests/bunit.core.tests/TestDoubles/PersistentComponentState/FakePersistentComponentStateTest.cs @@ -67,7 +67,7 @@ public void Test013(string key) { var fakeState = this.AddFakePersistentComponentState(); - fakeState.TryTake(key, out var actual).ShouldBeFalse(); + fakeState.TryTake(key, out _).ShouldBeFalse(); } [Fact(DisplayName = "TriggerOnPersisting triggers OnPersisting callbacks added to store")] diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index 89098229e..907ccba41 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0;net9.0 Bunit Bunit.Generator.Tests true @@ -26,7 +26,6 @@ - diff --git a/tests/bunit.testassets/BlazorE2E/AddRemoveChildComponents.razor b/tests/bunit.testassets/BlazorE2E/AddRemoveChildComponents.razor index 1c05a7166..ba4bca194 100644 --- a/tests/bunit.testassets/BlazorE2E/AddRemoveChildComponents.razor +++ b/tests/bunit.testassets/BlazorE2E/AddRemoveChildComponents.razor @@ -10,7 +10,7 @@ Child components follow. @code { int numAdded = 0; - List currentChildrenMessages = new List(); + private readonly List currentChildrenMessages = new List(); void AddChild() { diff --git a/tests/bunit.testassets/BlazorE2E/DataDashComponent.razor b/tests/bunit.testassets/BlazorE2E/DataDashComponent.razor index 435ef0fc0..06567bb4a 100644 --- a/tests/bunit.testassets/BlazorE2E/DataDashComponent.razor +++ b/tests/bunit.testassets/BlazorE2E/DataDashComponent.razor @@ -1,5 +1,5 @@
@TabId
@code { - string TabId = "17"; + private readonly string TabId = "17"; } diff --git a/tests/bunit.testassets/BlazorE2E/DuplicateAttributesComponent.razor b/tests/bunit.testassets/BlazorE2E/DuplicateAttributesComponent.razor index 20899324f..837da0f95 100644 --- a/tests/bunit.testassets/BlazorE2E/DuplicateAttributesComponent.razor +++ b/tests/bunit.testassets/BlazorE2E/DuplicateAttributesComponent.razor @@ -13,7 +13,7 @@
@functions { - Dictionary elementValues = new Dictionary + private readonly Dictionary elementValues = new Dictionary { { "bool", true }, { "string", "middle-value" }, diff --git a/tests/bunit.testassets/BlazorE2E/KeyPressEventComponent.razor b/tests/bunit.testassets/BlazorE2E/KeyPressEventComponent.razor index dd96e41d5..d5931408d 100644 --- a/tests/bunit.testassets/BlazorE2E/KeyPressEventComponent.razor +++ b/tests/bunit.testassets/BlazorE2E/KeyPressEventComponent.razor @@ -9,7 +9,7 @@ Type here: @code { - List keysPressed = new List(); + private readonly List keysPressed = new List(); void OnKeyPressed(KeyboardEventArgs eventArgs) { diff --git a/tests/bunit.testassets/BlazorE2E/MarkupBlockComponent.razor b/tests/bunit.testassets/BlazorE2E/MarkupBlockComponent.razor index ee7f9a185..1b9597319 100644 --- a/tests/bunit.testassets/BlazorE2E/MarkupBlockComponent.razor +++ b/tests/bunit.testassets/BlazorE2E/MarkupBlockComponent.razor @@ -28,7 +28,7 @@ #nullable disable bool changeOutput; - string myMarkup = "

This is a markup string.

"; + private readonly string myMarkup = "

This is a markup string.

"; void EmitMarkupBlock(RenderTreeBuilder builder) { diff --git a/tests/bunit.testassets/BlazorE2E/MovingCheckboxesComponent.razor b/tests/bunit.testassets/BlazorE2E/MovingCheckboxesComponent.razor index fbb1f4cf5..872b35bc2 100644 --- a/tests/bunit.testassets/BlazorE2E/MovingCheckboxesComponent.razor +++ b/tests/bunit.testassets/BlazorE2E/MovingCheckboxesComponent.razor @@ -41,7 +41,7 @@ @code { #nullable disable - List items = new List + private readonly List items = new List { new TodoItem { Text = "Alpha" }, new TodoItem { Text = "Beta" }, @@ -49,7 +49,7 @@ new TodoItem { Text = "Delta", IsDone = true }, }; - class TodoItem + private sealed class TodoItem { public bool IsDone { get; set; } public string Text { get; set; } diff --git a/tests/bunit.testassets/BlazorE2E/OrderedList.razor b/tests/bunit.testassets/BlazorE2E/OrderedList.razor index 96130629b..f7a712635 100644 --- a/tests/bunit.testassets/BlazorE2E/OrderedList.razor +++ b/tests/bunit.testassets/BlazorE2E/OrderedList.razor @@ -6,7 +6,7 @@ } @foreach (var item in Items) { - @Template(new Context { Index = index++, Item = item, }); + @Template(new Context { Index = index++, Item = item, }) } diff --git a/tests/bunit.testassets/RenderModes/ComponentThatPrintsAssignedRenderMode.razor b/tests/bunit.testassets/RenderModes/ComponentThatPrintsAssignedRenderMode.razor new file mode 100644 index 000000000..effe0ff2c --- /dev/null +++ b/tests/bunit.testassets/RenderModes/ComponentThatPrintsAssignedRenderMode.razor @@ -0,0 +1,9 @@ +@{ +#if NET9_0_OR_GREATER +} + +

Assigned Render Mode: @AssignedRenderMode?.GetType().Name

+ +@{ +#endif +} diff --git a/tests/bunit.testassets/RenderModes/ComponentWithChildContent.razor b/tests/bunit.testassets/RenderModes/ComponentWithChildContent.razor new file mode 100644 index 000000000..c8b0cd4e1 --- /dev/null +++ b/tests/bunit.testassets/RenderModes/ComponentWithChildContent.razor @@ -0,0 +1,6 @@ +@ChildContent +@code { + + [Parameter] public RenderFragment ChildContent { get; set; } = default!; + +} \ No newline at end of file diff --git a/tests/bunit.testassets/RenderModes/ComponentWithServerRenderMode.razor b/tests/bunit.testassets/RenderModes/ComponentWithServerRenderMode.razor new file mode 100644 index 000000000..dd008df6c --- /dev/null +++ b/tests/bunit.testassets/RenderModes/ComponentWithServerRenderMode.razor @@ -0,0 +1,15 @@ +@{ +#if NET9_0_OR_GREATER +} + +@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer +
@(ChildContent is not null ? "Parent assigned" : "Assigned") render mode: @AssignedRenderMode?.GetType().Name
+@ChildContent + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } +} + +@{ +#endif +} diff --git a/tests/bunit.testassets/RenderModes/ComponentWithWebAssemblyRenderMode.razor b/tests/bunit.testassets/RenderModes/ComponentWithWebAssemblyRenderMode.razor new file mode 100644 index 000000000..af0888317 --- /dev/null +++ b/tests/bunit.testassets/RenderModes/ComponentWithWebAssemblyRenderMode.razor @@ -0,0 +1,15 @@ +@{ +#if NET9_0_OR_GREATER +} + +@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveWebAssembly +
@(ChildContent is not null ? "Parent assigned" : "Assigned") render mode: @AssignedRenderMode?.GetType().Name
+@ChildContent + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } +} + +@{ +#endif +} \ No newline at end of file diff --git a/tests/bunit.testassets/RenderModes/ComponentWithoutRenderMode.razor b/tests/bunit.testassets/RenderModes/ComponentWithoutRenderMode.razor new file mode 100644 index 000000000..6d5f7f9b7 --- /dev/null +++ b/tests/bunit.testassets/RenderModes/ComponentWithoutRenderMode.razor @@ -0,0 +1,14 @@ +@{ +#if NET9_0_OR_GREATER +} + +
@(ChildContent is not null ? "Parent assigned" : "Assigned") render mode: @AssignedRenderMode?.GetType().Name
+@ChildContent + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } +} + +@{ +#endif +} \ No newline at end of file diff --git a/tests/bunit.testassets/RenderModes/RendererInfoComponent.cs b/tests/bunit.testassets/RenderModes/RendererInfoComponent.cs new file mode 100644 index 000000000..9393ab3eb --- /dev/null +++ b/tests/bunit.testassets/RenderModes/RendererInfoComponent.cs @@ -0,0 +1,20 @@ +#if NET9_0_OR_GREATER + +namespace Bunit.TestAssets.RenderModes; + +public class RendererInfoComponent : ComponentBase +{ + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenElement(0, "p"); + builder.AddContent(1, "Is interactive: "); + builder.AddContent(2, RendererInfo.IsInteractive); + builder.CloseElement(); + + builder.OpenElement(3, "p"); + builder.AddContent(4, "Rendermode: "); + builder.AddContent(5, RendererInfo.Name); + builder.CloseElement(); + } +} +#endif \ No newline at end of file diff --git a/tests/bunit.testassets/RenderModes/SectionOutletComponent.cs b/tests/bunit.testassets/RenderModes/SectionOutletComponent.cs new file mode 100644 index 000000000..70dd7d9a1 --- /dev/null +++ b/tests/bunit.testassets/RenderModes/SectionOutletComponent.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Components.Web; + +#if NET8_0_OR_GREATER +using Microsoft.AspNetCore.Components.Sections; +#endif + +namespace Bunit.TestAssets.RenderModes; + +public class SectionOutletComponent : ComponentBase +{ +#if NET8_0_OR_GREATER + private static readonly Guid SectionId = Guid.NewGuid(); +#endif + + [Parameter] public RenderFragment ChildContent { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { +#if NET8_0_OR_GREATER + builder.OpenComponent(0); + builder.AddComponentParameter(1, nameof(SectionOutlet.SectionId), SectionId); + builder.AddComponentRenderMode(RenderMode.InteractiveWebAssembly); + builder.CloseComponent(); + builder.OpenComponent(10); + builder.AddComponentParameter(11, nameof(SectionContent.SectionId), SectionId); + builder.AddAttribute(12, nameof(SectionContent.ChildContent), ChildContent); + builder.CloseComponent(); +#endif + } +} diff --git a/tests/bunit.testassets/SampleComponents/AsyncRenderChangesProperty.razor b/tests/bunit.testassets/SampleComponents/AsyncRenderChangesProperty.razor index e4368832a..4e68b7eb6 100644 --- a/tests/bunit.testassets/SampleComponents/AsyncRenderChangesProperty.razor +++ b/tests/bunit.testassets/SampleComponents/AsyncRenderChangesProperty.razor @@ -1,7 +1,7 @@ @code { - private TaskCompletionSource _tcs = new TaskCompletionSource(); + private readonly TaskCompletionSource _tcs = new TaskCompletionSource(); public int Counter; private Task Tick() diff --git a/tests/bunit.testassets/SampleComponents/ComponentWithRelativeUnitAsWidth.razor b/tests/bunit.testassets/SampleComponents/ComponentWithRelativeUnitAsWidth.razor new file mode 100644 index 000000000..69e5a86c7 --- /dev/null +++ b/tests/bunit.testassets/SampleComponents/ComponentWithRelativeUnitAsWidth.razor @@ -0,0 +1,2 @@ +
+
\ No newline at end of file diff --git a/tests/bunit.testassets/SampleComponents/InvokeAsyncInsideContinueWith.razor b/tests/bunit.testassets/SampleComponents/InvokeAsyncInsideContinueWith.razor index 9be4161b2..1abdf2cd8 100644 --- a/tests/bunit.testassets/SampleComponents/InvokeAsyncInsideContinueWith.razor +++ b/tests/bunit.testassets/SampleComponents/InvokeAsyncInsideContinueWith.razor @@ -22,13 +22,13 @@ { registeredTask = task; delegatedTask = task == null ? null : DelegateTo(task); - RenderWhenDone(); + _ = RenderWhenDone(); } base.OnParametersSet(); } - private async void RenderWhenDone() + private async Task RenderWhenDone() { var task = delegatedTask; if (task != null) @@ -44,7 +44,7 @@ private static async Task DelegateTo(Task task) { - await task;//.ConfigureAwait(false); + await task; return null; } } diff --git a/tests/bunit.testassets/SampleComponents/RenderOnClick.razor b/tests/bunit.testassets/SampleComponents/RenderOnClick.razor index d5ed71b16..b42b04388 100644 --- a/tests/bunit.testassets/SampleComponents/RenderOnClick.razor +++ b/tests/bunit.testassets/SampleComponents/RenderOnClick.razor @@ -2,8 +2,11 @@ @code { public int RenderCount { get; private set; } - - void IncreaseCount() { } + + void IncreaseCount() + { + // Left empty on purpose + } protected override void OnAfterRender(bool firstRender) => RenderCount++; } \ No newline at end of file diff --git a/tests/bunit.testassets/SampleComponents/SimpleUsingLocalizer.razor b/tests/bunit.testassets/SampleComponents/SimpleUsingLocalizer.razor index 9992ee117..d077d54b0 100644 --- a/tests/bunit.testassets/SampleComponents/SimpleUsingLocalizer.razor +++ b/tests/bunit.testassets/SampleComponents/SimpleUsingLocalizer.razor @@ -6,7 +6,7 @@ @code { protected override void OnInitialized() { - var localizedString = StringLocalizer["StringName"]; + _ = StringLocalizer["StringName"]; } } diff --git a/tests/bunit.testassets/SampleComponents/SimpleWithTemplate.razor b/tests/bunit.testassets/SampleComponents/SimpleWithTemplate.razor index 0ba4abec3..700fcdc83 100644 --- a/tests/bunit.testassets/SampleComponents/SimpleWithTemplate.razor +++ b/tests/bunit.testassets/SampleComponents/SimpleWithTemplate.razor @@ -1,7 +1,7 @@ @typeparam T @foreach (var d in Data) { - @Template?.Invoke(d); + @Template?.Invoke(d) } @code { diff --git a/tests/bunit.testassets/SampleComponents/ToggleableDetails.razor b/tests/bunit.testassets/SampleComponents/ToggleableDetails.razor index 5dcb306e3..b1cf9da25 100644 --- a/tests/bunit.testassets/SampleComponents/ToggleableDetails.razor +++ b/tests/bunit.testassets/SampleComponents/ToggleableDetails.razor @@ -3,7 +3,7 @@ {

Read the details carefully!

} -
+
Summary

Detailed content

diff --git a/tests/bunit.testassets/bunit.testassets.csproj b/tests/bunit.testassets/bunit.testassets.csproj index 424468974..183551378 100644 --- a/tests/bunit.testassets/bunit.testassets.csproj +++ b/tests/bunit.testassets/bunit.testassets.csproj @@ -20,6 +20,7 @@ + diff --git a/tests/bunit.web.tests/Asserting/MarkupMatchesTests.razor b/tests/bunit.web.tests/Asserting/MarkupMatchesTests.razor index d8723291f..d4b3bf5c4 100644 --- a/tests/bunit.web.tests/Asserting/MarkupMatchesTests.razor +++ b/tests/bunit.web.tests/Asserting/MarkupMatchesTests.razor @@ -1,13 +1,9 @@ -@using Bunit.TestAssets @using Bunit.TestAssets.SampleComponents @inherits TestContext @code { - private readonly ITestOutputHelper outputHelper; - - public MarkupMatchesTests(ITestOutputHelper outputHelper) + public MarkupMatchesTests() { - this.outputHelper = outputHelper; - TestContext.DefaultWaitTimeout = TimeSpan.FromSeconds(30); + DefaultWaitTimeout = TimeSpan.FromSeconds(30); } [Fact] diff --git a/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs b/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs index 412779aad..1e9e2e806 100644 --- a/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs +++ b/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs @@ -1,3 +1,6 @@ +using AngleSharp; +using AngleSharp.Css; +using AngleSharp.Dom; using Bunit.Rendering; namespace Bunit; @@ -66,5 +69,45 @@ public void Test021() cut.Instance.JSRuntime.ShouldNotBeNull(); } + + [Fact(DisplayName = "Searching first for derived component and then base component finds correct (#1691)")] + public void Test023() + { + var cut = RenderComponent( + ps => ps.AddChildContent() + .AddChildContent()); + + Should.NotThrow(() => + { + cut.FindComponents(); + cut.FindComponents(); + }); + } + + private class BaseComponent : ComponentBase + { + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.AddContent(0, "base"); + } + } + + private sealed class DerivedComponent : BaseComponent + { + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.AddContent(0, "derived"); + } + } #endif + + [Fact(DisplayName = "Using relative units in style attribute can be retrieved")] + public void Test022() + { + var cut = RenderComponent(); + + var text = cut.Find(".my-component").GetInnerText(); + + text.ShouldNotBeNull(); + } } diff --git a/tests/bunit.web.tests/TestDoubles/NavigationManager/FakeNavigationManagerTest.cs b/tests/bunit.web.tests/TestDoubles/NavigationManager/FakeNavigationManagerTest.cs index ca09b9423..1578dfe28 100644 --- a/tests/bunit.web.tests/TestDoubles/NavigationManager/FakeNavigationManagerTest.cs +++ b/tests/bunit.web.tests/TestDoubles/NavigationManager/FakeNavigationManagerTest.cs @@ -1,5 +1,6 @@ using System.Text.Json; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using Shouldly.ShouldlyExtensionMethods; namespace Bunit.TestDoubles; @@ -106,18 +107,18 @@ public void Test007() } #if !NET6_0_OR_GREATER - [Theory(DisplayName = "NavigateTo(uri, forceLoad) is saved in history")] - [InlineData("/uri", false)] - [InlineData("/anotherUri", true)] - public void Test100(string uri, bool forceLoad) - { - var sut = CreateFakeNavigationManager(); + [Theory(DisplayName = "NavigateTo(uri, forceLoad) is saved in history")] + [InlineData("/uri", false)] + [InlineData("/anotherUri", true)] + public void Test100(string uri, bool forceLoad) + { + var sut = CreateFakeNavigationManager(); - sut.NavigateTo(uri, forceLoad); + sut.NavigateTo(uri, forceLoad); - sut.History.ShouldHaveSingleItem() - .ShouldBeEquivalentTo(new NavigationHistory(uri, new NavigationOptions(forceLoad))); - } + sut.History.ShouldHaveSingleItem() + .ShouldBeEquivalentTo(new NavigationHistory(uri, new NavigationOptions(forceLoad))); + } #endif #if NET6_0_OR_GREATER [Theory(DisplayName = "NavigateTo(uri, forceLoad, replaceHistoryEntry) is saved in history")] @@ -135,8 +136,12 @@ public void Test200(string uri, bool forceLoad, bool replaceHistoryEntry) .ShouldBeEquivalentTo(new NavigationHistory(uri, new NavigationOptions { ForceLoad = forceLoad, ReplaceHistoryEntry = replaceHistoryEntry })); #elif NET7_0_OR_GREATER - var navigationOptions = new NavigationOptions { ForceLoad = forceLoad, ReplaceHistoryEntry = - replaceHistoryEntry }; + var navigationOptions = new NavigationOptions + { + ForceLoad = forceLoad, + ReplaceHistoryEntry = + replaceHistoryEntry + }; sut.History.ShouldHaveSingleItem() .ShouldBeEquivalentTo(new NavigationHistory(uri, navigationOptions, NavigationState.Succeeded)); #endif @@ -156,8 +161,11 @@ public void Test201() new NavigationOptions { ReplaceHistoryEntry = true })); #elif NET7_0_OR_GREATER sut.History.ShouldHaveSingleItem() - .ShouldBeEquivalentTo(new NavigationHistory("/secondUrl", new NavigationOptions { ReplaceHistoryEntry = - true }, NavigationState.Succeeded)); + .ShouldBeEquivalentTo(new NavigationHistory("/secondUrl", new NavigationOptions + { + ReplaceHistoryEntry = + true + }, NavigationState.Succeeded)); #endif } #endif @@ -230,7 +238,8 @@ public void Test013() var fakeNavigationManager = CreateFakeNavigationManager(); var requestOptions = new InteractiveRequestOptions { - ReturnUrl = "return", Interaction = InteractionType.SignIn, + ReturnUrl = "return", + Interaction = InteractionType.SignIn, }; requestOptions.TryAddAdditionalParameter("library", "bunit"); @@ -264,7 +273,7 @@ public void Test015() Should.Throw( () => fakeNavigationManager.History.Last().StateFromJson()); } - + [Fact(DisplayName = "Navigate to url with state should set that state on the NavigationManager")] public void Test016() { @@ -275,7 +284,7 @@ public void Test016() sut.HistoryEntryState.ShouldBe(State); } - + [Fact(DisplayName = "Navigate to url with force reload resets state")] public void Test017() { @@ -283,11 +292,32 @@ public void Test017() var sut = CreateFakeNavigationManager(); sut.NavigateTo("/internal", new NavigationOptions { HistoryEntryState = State }); - sut.NavigateTo("/internal", new NavigationOptions { HistoryEntryState = State, ForceLoad = true}); + sut.NavigateTo("/internal", new NavigationOptions { HistoryEntryState = State, ForceLoad = true }); sut.HistoryEntryState.ShouldBe(null); } + [Theory(DisplayName = "Preventing Navigation does not change Uri or BaseUri")] + [InlineData("/prevented-path")] + [InlineData("https://bunit.dev/uri")] + public void Test018(string navToUri) + { + var sut = CreateFakeNavigationManager(); + using var handler = sut.RegisterLocationChangingHandler(LocationChangingHandler); + + sut.NavigateTo(navToUri); + + sut.History.First().State.ShouldBe(NavigationState.Prevented); + sut.BaseUri.ShouldBe("http://localhost/"); + sut.Uri.ShouldBe("http://localhost/"); + + ValueTask LocationChangingHandler(LocationChangingContext arg) + { + arg.PreventNavigation(); + return ValueTask.CompletedTask; + } + } + private sealed class InterceptNavigateToCounterComponent : ComponentBase { protected override void BuildRenderTree(RenderTreeBuilder builder) diff --git a/version.json b/version.json index 1b260cc7a..3823e1a1d 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.36", + "version": "1.40", "assemblyVersion": { "precision": "revision" },
- - @Progress-Telerik + + @syncfusion
- Progress Telerik + Syncfusion
- - @syncfusion + + @JetBrainsOfficial
- Syncfusion + JetBrains