diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5a52b66ad..cd53d67a6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- main
+ - v3
paths:
- 'src/**'
- 'tests/**'
@@ -44,8 +45,6 @@ jobs:
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
- 8.0.x
- 9.0.x
10.0.x
11.0.x
@@ -126,8 +125,6 @@ jobs:
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
- 8.0.x
- 9.0.x
10.0.x
11.0.x
@@ -225,8 +222,6 @@ jobs:
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
- 8.0.x
- 9.0.x
10.0.x
11.0.x
@@ -256,7 +251,7 @@ jobs:
- name: 🧪 Run sample unit tests
working-directory: docs/samples
- run: dotnet test samples.sln -p:VSTestUseMSBuildOutput=false -f net8.0
+ run: dotnet test samples.sln -p:VSTestUseMSBuildOutput=false -f net10.0
- name: 📄 Build docs
working-directory: ./docs/site
diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml
index 35f3bbdfe..7b9be3aa8 100644
--- a/.github/workflows/docs-deploy.yml
+++ b/.github/workflows/docs-deploy.yml
@@ -73,8 +73,6 @@ jobs:
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
- 8.0.x
- 9.0.x
10.0.x
11.0.x
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c6284e036..72d934d0a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -63,8 +63,6 @@ jobs:
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
- 8.0.x
- 9.0.x
10.0.x
11.0.x
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 93b2ce6d4..8de7acf21 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -26,36 +26,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/README.md b/README.md
index 4208f1d12..fda024d9f 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,8 @@ To get started, head to the [getting started documentation](https://bunit.dev/do
| bUnit version | .NET version |
| ------------- | ------------------------------- |
| 1.x | ≥ .netcore3.1 && ≤ .net 9 |
-| 2.x | ≥ .net8 |
+| 2.x | ≥ .net8 && ≤ .net 11 |
+| 3.x | ≥ .net10 |
For a migration guide, see [Upgrading bUnit](https://bunit.dev/docs/migrations/index.html).
diff --git a/docs/samples/components/bunit.docs.samples.csproj b/docs/samples/components/bunit.docs.samples.csproj
index 88949209f..9e1e852d7 100644
--- a/docs/samples/components/bunit.docs.samples.csproj
+++ b/docs/samples/components/bunit.docs.samples.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0;net10.0;net11.0
+ net10.0;net11.0Bunit.Docs.SamplesenableCA1014,NU5104
diff --git a/docs/samples/tests/Directory.Build.props b/docs/samples/tests/Directory.Build.props
index 8071ce9d1..9a5722898 100644
--- a/docs/samples/tests/Directory.Build.props
+++ b/docs/samples/tests/Directory.Build.props
@@ -1,6 +1,6 @@
- net8.0;net9.0;net10.0;net11.0
+ net10.0;net11.0falsetruefalse
diff --git a/docs/site/docs/extensions/bunit-generators.md b/docs/site/docs/extensions/bunit-generators.md
index c9c072908..74d9b3f97 100644
--- a/docs/site/docs/extensions/bunit-generators.md
+++ b/docs/site/docs/extensions/bunit-generators.md
@@ -53,12 +53,12 @@ To use the generator, the **Interceptor** feature has to be used inside the cspr
```xml
- net8.0
+ net10.0$(InterceptorsPreviewNamespaces);Bunit
```
-Due to the usage of **Interceptors** the generator is only available for .NET 8.0 and above. The generator does create a `partial` class, so it can be extended with custom logic if needed.
+Due to the usage of **Interceptors** the generator requires .NET 10.0 and above, matching bUnit's minimum supported target framework. The generator does create a `partial` class, so it can be extended with custom logic if needed.
## Component stub generator via `ComponentStubAttribute`
diff --git a/docs/site/docs/getting-started/create-test-project.md b/docs/site/docs/getting-started/create-test-project.md
index a3b526ada..f1b9c558a 100644
--- a/docs/site/docs/getting-started/create-test-project.md
+++ b/docs/site/docs/getting-started/create-test-project.md
@@ -148,10 +148,10 @@ dotnet add package bunit --version #{NBGV_NuGetPackageVersion}#
The test projects setting needs to be set to the following:
- the project's SDK needs to be set to `Microsoft.NET.Sdk.Razor` (this does not work with **TUnit** - a more detailed explanation can be found below)
-- set the `` to `net8.0`
+- set the `` to `net10.0`
> [!NOTE]
-> bUnit works with `net8.0` and above as well.
+> bUnit works with `net10.0` and above as well.
To do so, change the first part of the test projects `.csproj` file to look like this.:
@@ -159,7 +159,7 @@ To do so, change the first part of the test projects `.csproj` file to look like
- net8.0
+ net10.0 ...
@@ -182,7 +182,7 @@ The result should be a test project with a `.csproj` that looks like this (non b
- net8.0
+ net10.0enablefalse
@@ -211,7 +211,7 @@ The result should be a test project with a `.csproj` that looks like this (non b
- net8.0
+ net10.0enablefalseExe
@@ -241,7 +241,7 @@ The result should be a test project with a `.csproj` that looks like this (non b
- net8.0
+ net10.0enablefalse
@@ -267,7 +267,7 @@ The result should be a test project with a `.csproj` that looks like this (non b
- net8.0
+ net10.0enablefalse
@@ -293,7 +293,7 @@ The result should be a test project with a `.csproj` that looks like this (non b
- net8.0
+ net10.0enablefalsefalse
diff --git a/docs/site/docs/interaction/render-modes.md b/docs/site/docs/interaction/render-modes.md
index d019abba6..45ca1d70f 100644
--- a/docs/site/docs/interaction/render-modes.md
+++ b/docs/site/docs/interaction/render-modes.md
@@ -75,7 +75,7 @@ public void StaticRendering()
# [Razor test code](#tab/razor)
```razor
-@inherits TestContext
+@inherits BunitContext
@code {
[Fact]
public void InteractiveServer()
@@ -108,7 +108,7 @@ public void StaticRendering()
***
## 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`.
+To control the `ComponentBase.RendererInfo` property during testing, use the method on the `BunitContext` 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:
@@ -154,4 +154,4 @@ public void SimulatingInteractiveServerRendering()
```
> [!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
+> If a component under test uses the `ComponentBase.RendererInfo` property and the `SetRendererInfo` on `BunitContext` 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/migrations/2to3.md b/docs/site/docs/migrations/2to3.md
new file mode 100644
index 000000000..0ee106ebe
--- /dev/null
+++ b/docs/site/docs/migrations/2to3.md
@@ -0,0 +1,51 @@
+---
+uid: migrating-from-2-to-3
+title: Migrating from 2.x to 3.x
+---
+
+# Migrating from bUnit 2.x to 3.x
+This document describes the changes made in bUnit 3.x that may affect existing tests written for bUnit 2.x.
+
+## Dropped support for .NET 8 and .NET 9
+
+bUnit 3.x drops support for `net8.0` and `net9.0`. The minimum supported target framework is now `net10.0`, and `net11.0` is also supported.
+
+To migrate, update your test project's `` (or ``) to `net10.0` or later:
+
+```diff
+- net8.0
++ net10.0
+```
+
+## Removed obsolete and deprecated APIs
+
+bUnit 3.x removes APIs that were kept around for backwards compatibility with bUnit 1.x and were marked `[Obsolete]` or scheduled for removal:
+
+- `TestContext` class: use `BunitContext` instead.
+- `RenderComponent()` and `RenderComponent(Action>?)`: use `Render()` instead.
+- `BunitSignOutSessionStateManager`: this was a test double for the obsolete `SignOutSessionStateManager` from `Microsoft.AspNetCore.Components.WebAssembly.Authentication` and has been removed. `AddAuthorization()` no longer registers it in the service collection.
+
+## `BunitContext.DefaultWaitTimeout` is no longer static
+
+`BunitContext.DefaultWaitTimeout` used to be a `static` property, so setting it in one test affected the timeout used by every other test running in the same process, including tests running concurrently. It is now an instance property, so each `BunitContext` (and each test class deriving from it) has its own timeout, used by both `WaitForXXX` helpers and its own `JSInterop`.
+
+To migrate, set the timeout on the test's `BunitContext` instance instead of the static property. If every test in a class needs a longer timeout, the constructor is a good place to set it once:
+
+```diff
+ public class MyComponentTest : BunitContext
+ {
++ public MyComponentTest()
++ {
++ DefaultWaitTimeout = TimeSpan.FromSeconds(10);
++ }
+
+ [Fact]
+ public void MyTest()
+ {
+- BunitContext.DefaultWaitTimeout = TimeSpan.FromSeconds(10);
+ // ...
+ }
+ }
+```
+
+Setting it on a specific `BunitContext` instance (rather than a class deriving from it) works the same way: `context.DefaultWaitTimeout = TimeSpan.FromSeconds(10);`.
\ No newline at end of file
diff --git a/docs/site/docs/migrations/index.md b/docs/site/docs/migrations/index.md
index 35f8abeae..4969e3e43 100644
--- a/docs/site/docs/migrations/index.md
+++ b/docs/site/docs/migrations/index.md
@@ -7,4 +7,5 @@ title: Migrations
This section covers the migration across major versions of bUnit. The mirations themselves also list the link to the old version of the documentation (including the old API documentation).
+ -
-
\ No newline at end of file
diff --git a/docs/site/docs/providing-input/seeding-assets.md b/docs/site/docs/providing-input/seeding-assets.md
index af523698a..d821c04a8 100644
--- a/docs/site/docs/providing-input/seeding-assets.md
+++ b/docs/site/docs/providing-input/seeding-assets.md
@@ -5,7 +5,7 @@ title: Seeding static assets (Assets)
# Seeding static assets (`Assets`)
-This article explains how to seed the `Assets` property of components under test in bUnit. This is supported for .NET 9 and later.
+This article explains how to seed the `Assets` property of components under test in bUnit.
Since .NET 9, components can access static assets mapped by [`MapStaticAssets`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/map-static-files?view=aspnetcore-9.0) through the [`ComponentBase.Assets`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.componentbase.assets?view=aspnetcore-9.0) property, e.g. to resolve fingerprinted URLs:
diff --git a/docs/site/docs/toc.md b/docs/site/docs/toc.md
index 53fe7e811..982f84898 100644
--- a/docs/site/docs/toc.md
+++ b/docs/site/docs/toc.md
@@ -37,6 +37,7 @@
## [bunit.generators](xref:bunit-generators)
# [Migrations](xref:migrations)
+## [2.x to 3.x](xref:migrating-from-2-to-3)
## [1.x to 2.x](xref:migrating-from-1-to-2)
# [Miscellaneous testing tips](xref:misc-test-tips)
diff --git a/src/bunit.generators.internal/README.md b/src/bunit.generators.internal/README.md
index 76af51e46..3031defd9 100644
--- a/src/bunit.generators.internal/README.md
+++ b/src/bunit.generators.internal/README.md
@@ -9,5 +9,5 @@ dotnet build-server shutdown
A good way to quickly see if the generate is producing output:
```
-dotnet build-server shutdown && dotnet clean && dotnet test -p:TargetFramework=net8.0
+dotnet build-server shutdown && dotnet clean && dotnet test -p:TargetFramework=net10.0
```
diff --git a/src/bunit.generators/README.md b/src/bunit.generators/README.md
index 5211e1bfd..b852bfa0f 100644
--- a/src/bunit.generators/README.md
+++ b/src/bunit.generators/README.md
@@ -45,12 +45,12 @@ To use the generator, the **Interceptor** feature has to be used inside the cspr
```xml
- net8.0
+ net10.0$(InterceptorsPreviewNamespaces);Bunit
```
-This limits the usage to .NET 8 and above.
+This limits the usage to .NET 10 and above, matching bUnit's minimum supported target framework.
## `ComponentStubAttribute`
This generator adds the ability to automatically generate stubs for a given type via an attribute.
diff --git a/src/bunit.template/template/.template.config/dotnetcli.host.json b/src/bunit.template/template/.template.config/dotnetcli.host.json
index ced81d900..d51839f87 100644
--- a/src/bunit.template/template/.template.config/dotnetcli.host.json
+++ b/src/bunit.template/template/.template.config/dotnetcli.host.json
@@ -14,9 +14,9 @@
}
},
"usageExamples": [
- "--framework xunit --sdk net8.0",
- "--framework xunitv3 --sdk net8.0",
- "--framework nunit --sdk net8.0",
- "--framework mstest --sdk net8.0"
+ "--framework xunit --sdk net10.0",
+ "--framework xunitv3 --sdk net10.0",
+ "--framework nunit --sdk net10.0",
+ "--framework mstest --sdk net10.0"
]
}
diff --git a/src/bunit.template/template/.template.config/template.json b/src/bunit.template/template/.template.config/template.json
index 90eab74ea..27b894907 100644
--- a/src/bunit.template/template/.template.config/template.json
+++ b/src/bunit.template/template/.template.config/template.json
@@ -94,16 +94,6 @@
"defaultValue": "net10.0",
"replaces": "targetSdk",
"choices": [
- {
- "choice": "net8.0",
- "description": ".net 8.0",
- "displayName": ".net 8.0"
- },
- {
- "choice": "net9.0",
- "description": ".net 9.0",
- "displayName": ".net 9.0"
- },
{
"choice": "net10.0",
"description": ".net 10.0",
diff --git a/src/bunit.template/template/CounterCSharpTest.cs b/src/bunit.template/template/CounterCSharpTest.cs
index fd7c78040..a9c7b4835 100644
--- a/src/bunit.template/template/CounterCSharpTest.cs
+++ b/src/bunit.template/template/CounterCSharpTest.cs
@@ -7,7 +7,6 @@ namespace Company.BlazorTests1;
#if (testFramework_xunit)
public class CounterCSharpTest : BunitContext
#elif (testFramework_xunitv3)
-// The full qualified namespace for bUnit TestContext is used here as xunit v3 also offers a TestContext class
public class CounterCSharpTest : BunitContext
#elif (testFramework_nunit)
public class CounterCSharpTest : BunitContext
diff --git a/src/bunit.template/template/CounterRazorTests.razor b/src/bunit.template/template/CounterRazorTests.razor
index 8b02e77ce..2126a27e2 100644
--- a/src/bunit.template/template/CounterRazorTests.razor
+++ b/src/bunit.template/template/CounterRazorTests.razor
@@ -1,7 +1,6 @@
@*#if (testFramework_xunit) *@
@inherits BunitContext
@*#elif (testFramework_xunitv3)*@
-@* The full qualified namespace for bUnit TestContext is used here as xunit v3 also offers a TestContext class *@
@inherits BunitContext
@*#elif (testFramework_nunit)*@
@inherits BunitContext
diff --git a/src/bunit.web.query/bunit.web.query.csproj b/src/bunit.web.query/bunit.web.query.csproj
index c0e9bc642..2a5d108a0 100644
--- a/src/bunit.web.query/bunit.web.query.csproj
+++ b/src/bunit.web.query/bunit.web.query.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0;net10.0;net11.0
+ net10.0;net11.0BunitBunit.Web.Query
diff --git a/src/bunit/BunitContext.Obsoletes.cs b/src/bunit/BunitContext.Obsoletes.cs
deleted file mode 100644
index 121e95636..000000000
--- a/src/bunit/BunitContext.Obsoletes.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace Bunit;
-
-public partial class BunitContext
-{
- ///
- /// Use instead.
- ///
- [Obsolete($"Use {nameof(Render)} instead.", true, UrlFormat = "https://bunit.dev/docs/migrations")]
- public IRenderedComponent RenderComponent()
- where TComponent : IComponent
- {
- throw new NotSupportedException($"Use {nameof(Render)}<{typeof(TComponent).Name}> instead.");
- }
-
- ///
- /// Use instead.
- ///
- [Obsolete($"Use {nameof(Render)} instead.", true, UrlFormat = "https://bunit.dev/docs/migrations")]
- public IRenderedComponent RenderComponent(Action>? parameterBuilder)
- where TComponent : IComponent
- {
- throw new NotSupportedException($"Use {nameof(Render)}<{typeof(TComponent).Name}> instead.");
- }
-}
diff --git a/src/bunit/BunitContext.cs b/src/bunit/BunitContext.cs
index 6783f63c8..5671287f1 100644
--- a/src/bunit/BunitContext.cs
+++ b/src/bunit/BunitContext.cs
@@ -1,6 +1,4 @@
-#if NET9_0_OR_GREATER
using System.Runtime.CompilerServices;
-#endif
using Bunit.Extensions;
using Bunit.Rendering;
using Microsoft.Extensions.Logging;
@@ -16,11 +14,10 @@ public partial class BunitContext : IDisposable, IAsyncDisposable
private BunitRenderer? bunitRenderer;
///
- /// Gets or sets the default wait timeout used by "WaitFor" operations, i.e. ,
- /// and JSInterop invocation handlers that have not been configured with results.
+ /// Gets or sets the default wait timeout used by "WaitFor" operations and JSInterop invocation
+ /// handlers that have not been configured with results. Default is 1 second.
///
- /// The default is 1 second.
- public static TimeSpan DefaultWaitTimeout { get; set; } = TimeSpan.FromSeconds(1);
+ public TimeSpan DefaultWaitTimeout { get; set; } = TimeSpan.FromSeconds(1);
///
/// Gets the renderer used by the test context.
@@ -65,6 +62,7 @@ public BunitContext()
Services = new BunitServiceProvider();
Services.AddSingleton(_ => ComponentFactories);
Services.AddDefaultBunitContextServices(this, JSInterop);
+ JSInterop.AttachToContext(this);
}
///
@@ -161,9 +159,7 @@ public virtual IRenderedComponent Render(ActionThe type of component to find in the render tree.
/// The render fragment to render.
/// The .
-#if NET9_0_OR_GREATER
[OverloadResolutionPriority(1)]
-#endif
public virtual IRenderedComponent Render(RenderFragment renderFragment)
where TComponent : IComponent
=> RenderInsideRenderTree(renderFragment);
@@ -176,7 +172,6 @@ public virtual IRenderedComponent Render(RenderFragment
public virtual IRenderedComponent Render(RenderFragment renderFragment)
=> RenderInsideRenderTree(renderFragment);
-#if NET9_0_OR_GREATER
///
/// Sets the for the renderer.
///
@@ -201,7 +196,6 @@ public void SetRendererInfo(RendererInfo? rendererInfo)
[SuppressMessage("Design", "CA1054:URI-like parameters should not be strings", Justification = "Using string to align with ResourceAsset")]
public void AddAsset(string url, string? label = null, params ResourceAssetProperty[] properties)
=> Renderer.AddAsset(url, label, properties);
-#endif
///
/// Dummy method required to allow Blazor's compiler to generate
diff --git a/src/bunit/ComponentParameterCollection.cs b/src/bunit/ComponentParameterCollection.cs
index bae0ece4e..43bb31f68 100644
--- a/src/bunit/ComponentParameterCollection.cs
+++ b/src/bunit/ComponentParameterCollection.cs
@@ -19,13 +19,11 @@ internal class ComponentParameterCollection : ICollection, I
///
public bool IsReadOnly => false;
-#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.
@@ -108,9 +106,7 @@ 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/ComponentParameterCollectionBuilder.cs b/src/bunit/ComponentParameterCollectionBuilder.cs
index 10ff1e4bc..e623aaa82 100644
--- a/src/bunit/ComponentParameterCollectionBuilder.cs
+++ b/src/bunit/ComponentParameterCollectionBuilder.cs
@@ -427,7 +427,6 @@ static string TrimEnd(string source, string value)
: source;
}
-#if NET9_0_OR_GREATER
///
/// Sets (or unsets) the for the component and child components.
///
@@ -438,7 +437,6 @@ public ComponentParameterCollectionBuilder SetAssignedRenderMode(ICo
parameters.RenderMode = renderMode;
return this;
}
-#endif
///
/// Try to add a for a parameter with the , if
diff --git a/src/bunit/Extensions/WaitForHelpers/WaitForFailedException.cs b/src/bunit/Extensions/WaitForHelpers/WaitForFailedException.cs
index ec18da2ff..9099ec25c 100644
--- a/src/bunit/Extensions/WaitForHelpers/WaitForFailedException.cs
+++ b/src/bunit/Extensions/WaitForHelpers/WaitForFailedException.cs
@@ -38,7 +38,7 @@ time to complete the necessary number of renders of the component under test.
and see if they match what is expected. If they do not,
consider increasing the timeout, either at the individual
method call level, e.g. WaitForElement("div", TimeSpan.FromSeconds(15)),
- or via the static TestContext.DefaultWaitTimeout property.
+ or via the BunitContext.DefaultWaitTimeout property.
Check count: {checkCount}.
Component render count: {componentRenderCount}.
diff --git a/src/bunit/Extensions/WaitForHelpers/WaitForHelper.cs b/src/bunit/Extensions/WaitForHelpers/WaitForHelper.cs
index d8e5111e1..f96d019e5 100644
--- a/src/bunit/Extensions/WaitForHelpers/WaitForHelper.cs
+++ b/src/bunit/Extensions/WaitForHelpers/WaitForHelper.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using Bunit.Rendering;
using Microsoft.Extensions.Logging;
@@ -15,6 +16,9 @@ internal abstract class WaitForHelper : IDisposable
private readonly Func> completeChecker;
private readonly IRenderedComponent renderedComponent;
private readonly ILogger> logger;
+
+ [SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "Owned by the test, not by this helper.")]
+ private readonly BunitContext bunitContext;
private readonly BunitRenderer renderer;
private readonly Timer? timer;
private bool isDisposed;
@@ -76,10 +80,8 @@ protected WaitForHelper(
this.completeChecker = completeChecker ?? throw new ArgumentNullException(nameof(completeChecker));
logger = renderedComponent.Services.CreateLogger>();
- renderer = renderedComponent
- .Services
- .GetRequiredService()
- .Renderer;
+ bunitContext = renderedComponent.Services.GetRequiredService();
+ renderer = bunitContext.Renderer;
checkPassedCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
// Create the wait task and run the initial check
@@ -280,11 +282,11 @@ private void SubscribeToOnAfterRender()
renderedComponent.OnAfterRender += OnAfterRender;
}
- private static TimeSpan GetRuntimeTimeout(TimeSpan? timeout)
+ private TimeSpan GetRuntimeTimeout(TimeSpan? timeout)
{
return Debugger.IsAttached
? Timeout.InfiniteTimeSpan
- : timeout ?? BunitContext.DefaultWaitTimeout;
+ : timeout ?? bunitContext.DefaultWaitTimeout;
}
private static Func> WrapSynchronousChecker(Func<(bool CheckPassed, T Content)> completeChecker)
diff --git a/src/bunit/JSInterop/BunitJSInterop.cs b/src/bunit/JSInterop/BunitJSInterop.cs
index 2dbaa9787..3f466aa1c 100644
--- a/src/bunit/JSInterop/BunitJSInterop.cs
+++ b/src/bunit/JSInterop/BunitJSInterop.cs
@@ -10,6 +10,8 @@ public class BunitJSInterop
{
private readonly Dictionary> handlers = new();
private JSRuntimeMode mode;
+ private BunitContext? owningContext;
+ private TimeSpan standaloneDefaultWaitTimeout = TimeSpan.FromSeconds(1);
///
/// Gets a dictionary of all this mock has observed.
@@ -27,6 +29,27 @@ public class BunitJSInterop
///
public IJSRuntime JSRuntime { get; }
+ ///
+ /// Gets or sets the timeout used by unconfigured invocation handlers. Defers to the owning
+ /// when attached.
+ ///
+ internal TimeSpan DefaultWaitTimeout
+ {
+ get => owningContext?.DefaultWaitTimeout ?? standaloneDefaultWaitTimeout;
+ set
+ {
+ if (owningContext is not null)
+ owningContext.DefaultWaitTimeout = value;
+ else
+ standaloneDefaultWaitTimeout = value;
+ }
+ }
+
+ ///
+ /// Attaches this instance to its owning .
+ ///
+ internal void AttachToContext(BunitContext context) => owningContext = context;
+
///
/// Initializes a new instance of the class.
///
@@ -51,6 +74,7 @@ public void AddInvocationHandler(JSRuntimeInvocationHandlerBase());
handlers[resultType].Add(handler);
+ handler.AttachTo(this);
}
internal ValueTask HandleInvocation(JSRuntimeInvocation invocation)
diff --git a/src/bunit/JSInterop/Implementation/BunitJSObjectReference.cs b/src/bunit/JSInterop/Implementation/BunitJSObjectReference.cs
index fdaaac958..7566f57fc 100644
--- a/src/bunit/JSInterop/Implementation/BunitJSObjectReference.cs
+++ b/src/bunit/JSInterop/Implementation/BunitJSObjectReference.cs
@@ -4,9 +4,6 @@ namespace Bunit;
[SuppressMessage("Minor Code Smell", "S1939:Inheritance list should not be redundant", Justification = "By design. To make it obvious that both is implemented.")]
internal sealed class BunitJSObjectReference : IJSObjectReference, IJSInProcessObjectReference
-#if !NET9_0_OR_GREATER
- , IJSUnmarshalledObjectReference
-#endif
{
private BunitJSInterop JSInterop { get; }
@@ -23,7 +20,6 @@ public ValueTask InvokeAsync(string identifier, object?[]? args)
public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args)
=> JSInterop.HandleInvokeAsync(identifier, cancellationToken, args);
-#if NET10_0_OR_GREATER
///
public ValueTask InvokeConstructorAsync(string identifier, object?[]? args)
=> JSInterop.HandleInvokeConstructorAsync(identifier, args);
@@ -49,7 +45,6 @@ public ValueTask InvokeConstructorAsync(string identifier, C
///
public void SetValue(string identifier, TValue value) => throw new NotImplementedException();
-#endif
///
public TValue Invoke(string identifier, params object?[]? args)
diff --git a/src/bunit/JSInterop/Implementation/BunitJSRuntime.net10.cs b/src/bunit/JSInterop/Implementation/BunitJSRuntime.net10.cs
index 5fd991935..3a3e1c3d9 100644
--- a/src/bunit/JSInterop/Implementation/BunitJSRuntime.net10.cs
+++ b/src/bunit/JSInterop/Implementation/BunitJSRuntime.net10.cs
@@ -1,4 +1,3 @@
-#if NET10_0_OR_GREATER
using Bunit.JSInterop.Implementation;
namespace Bunit.JSInterop;
@@ -16,4 +15,3 @@ ValueTask IJSRuntime.InvokeConstructorAsync(string identifie
ValueTask IJSRuntime.InvokeConstructorAsync(string identifier, CancellationToken cancellationToken, object?[]? args)
=> JSInterop.HandleInvokeConstructorAsync(identifier, cancellationToken, args);
}
-#endif
diff --git a/src/bunit/JSInterop/Implementation/BunitJSRuntime.net8.cs b/src/bunit/JSInterop/Implementation/BunitJSRuntime.net8.cs
deleted file mode 100644
index 1a3a8d649..000000000
--- a/src/bunit/JSInterop/Implementation/BunitJSRuntime.net8.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-#if !NET9_0_OR_GREATER
-using Bunit.JSInterop.Implementation;
-
-namespace Bunit.JSInterop;
-
-///
-/// bUnit's implementation of .
-///
-internal sealed partial class BunitJSRuntime : IJSUnmarshalledRuntime
-{
- ///
- public TResult InvokeUnmarshalled(string identifier)
- => JSInterop.HandleInvokeUnmarshalled(identifier);
-
- ///
- public TResult InvokeUnmarshalled(string identifier, T0 arg0)
- => JSInterop.HandleInvokeUnmarshalled(identifier, arg0);
-
- ///
- public TResult InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1)
- => JSInterop.HandleInvokeUnmarshalled(identifier, arg0, arg1);
-
- ///
- public TResult InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2)
- => JSInterop.HandleInvokeUnmarshalled(identifier, arg0, arg1, arg2);
-}
-#endif
diff --git a/src/bunit/JSInterop/Implementation/JSRuntimeExtensions.cs b/src/bunit/JSInterop/Implementation/JSRuntimeExtensions.cs
index e5d9f5159..075043e2f 100644
--- a/src/bunit/JSInterop/Implementation/JSRuntimeExtensions.cs
+++ b/src/bunit/JSInterop/Implementation/JSRuntimeExtensions.cs
@@ -85,7 +85,6 @@ internal static TResult HandleInvokeUnmarshalled(this Bunit
.GetResult();
}
-#if NET10_0_OR_GREATER
internal static ValueTask HandleInvokeConstructorAsync(this BunitJSInterop jSInterop, string identifier, object?[]? args)
{
var invocation = new JSRuntimeInvocation(identifier, null, args, typeof(IJSObjectReference), "InvokeConstructorAsync");
@@ -98,7 +97,6 @@ internal static ValueTask HandleInvokeConstructorAsync(this
var invocation = new JSRuntimeInvocation(identifier, cancellationToken, args, typeof(IJSObjectReference), "InvokeConstructorAsync");
return jSInterop.HandleInvocation(invocation);
}
-#endif
private static string GetInvokeAsyncMethodName()
=> typeof(TValue) == typeof(Microsoft.JSInterop.Infrastructure.IJSVoidResult)
diff --git a/src/bunit/JSInterop/InvocationHandlers/JSRuntimeInvocationHandlerBase{TResult}.cs b/src/bunit/JSInterop/InvocationHandlers/JSRuntimeInvocationHandlerBase{TResult}.cs
index 53026ff4d..949d4022d 100644
--- a/src/bunit/JSInterop/InvocationHandlers/JSRuntimeInvocationHandlerBase{TResult}.cs
+++ b/src/bunit/JSInterop/InvocationHandlers/JSRuntimeInvocationHandlerBase{TResult}.cs
@@ -16,6 +16,7 @@ public abstract class JSRuntimeInvocationHandlerBase : IDisposable
private long nextInvocationId;
private Task? outcome;
private bool disposed;
+ private BunitJSInterop? owner;
///
/// Gets a value indicating whether this handler is set up to handle calls to InvokeVoidAsync(string, object[]).
@@ -79,7 +80,7 @@ protected internal virtual Task HandleAsync(JSRuntimeInvocation invocat
if (Volatile.Read(ref outcome) is { } configured)
return configured;
- var timeout = BunitContext.DefaultWaitTimeout;
+ var timeout = DefaultWaitTimeout;
if (timeout <= TimeSpan.Zero)
{
throw new JSRuntimeInvocationNotSetException(invocation);
@@ -108,6 +109,11 @@ protected internal virtual Task HandleAsync(JSRuntimeInvocation invocat
/// True if the handler can handle the invocation, false otherwise.
internal bool CanHandle(JSRuntimeInvocation invocation) => invocationMatcher(invocation);
+ ///
+ /// Attaches this handler to its owning .
+ ///
+ internal void AttachTo(BunitJSInterop jsInterop) => owner = jsInterop;
+
///
public void Dispose()
{
@@ -144,6 +150,8 @@ private void CompleteAll(Task next)
}
}
+ private TimeSpan DefaultWaitTimeout => owner?.DefaultWaitTimeout ?? TimeSpan.FromSeconds(1);
+
private void OnTimeoutElapsed(object? state)
{
if (state is not PendingInvocation pending || !pendingInvocations.TryRemove(pending.Id, out _))
diff --git a/src/bunit/RemovedInFutureVersionAttribute.cs b/src/bunit/RemovedInFutureVersionAttribute.cs
deleted file mode 100644
index 3330d9252..000000000
--- a/src/bunit/RemovedInFutureVersionAttribute.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Bunit;
-
-[AttributeUsage(AttributeTargets.All, Inherited = false)]
-internal sealed class RemovedInFutureVersionAttribute(string todo) : Attribute
-{
- public string Todo { get; } = todo;
-}
diff --git a/src/bunit/Rendering/BunitHtmlParser.cs b/src/bunit/Rendering/BunitHtmlParser.cs
index 47fcf0095..7973adeb1 100644
--- a/src/bunit/Rendering/BunitHtmlParser.cs
+++ b/src/bunit/Rendering/BunitHtmlParser.cs
@@ -1,5 +1,6 @@
using System.Collections;
using System.Diagnostics;
+using System.Threading;
using AngleSharp;
using AngleSharp.Css;
using AngleSharp.Dom;
@@ -23,7 +24,7 @@ internal sealed class BunitHtmlParser : IDisposable
private readonly IBrowsingContext context;
private readonly HtmlParser htmlParser;
private readonly List documents = new();
- private readonly object parserLock = new();
+ private readonly Lock parserLock = new();
private bool disposed;
///
diff --git a/src/bunit/Rendering/BunitRenderer.cs b/src/bunit/Rendering/BunitRenderer.cs
index 88740fcbc..12d9bb375 100644
--- a/src/bunit/Rendering/BunitRenderer.cs
+++ b/src/bunit/Rendering/BunitRenderer.cs
@@ -4,6 +4,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
+using System.Threading;
namespace Bunit.Rendering;
@@ -23,7 +24,7 @@ public sealed class BunitRenderer : Renderer
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SetDirectParameters")]
private static extern void CallSetDirectParameters(ComponentState componentState, ParameterView parameters);
- private readonly object renderTreeUpdateLock = new();
+ private readonly Lock renderTreeUpdateLock = new();
private readonly HashSet returnedRenderedComponentIds = new();
private readonly List rootComponents = new();
@@ -60,7 +61,6 @@ private bool IsBatchInProgress
///
internal int RenderCount { get; }
-#if NET9_0_OR_GREATER
private RendererInfo? rendererInfo;
///
@@ -125,7 +125,6 @@ public void AddAsset(string url, string? label = null, params ResourceAssetPrope
resourceAssets.Add(new ResourceAsset(url, props.Count > 0 ? props : null));
resourceAssetCollection = null;
}
-#endif
///
/// Initializes a new instance of the class.
@@ -344,7 +343,6 @@ protected override IComponent ResolveComponentForRenderMode(Type componentType,
return componentActivator.CreateInstance(componentType);
}
-#if NET9_0_OR_GREATER
///
protected override IComponentRenderMode? GetComponentRenderMode(IComponent component)
{
@@ -417,7 +415,6 @@ childRenderMode is not null
return null;
}
}
-#endif
///
protected override void AddPendingTask(ComponentState? componentState, Task task)
diff --git a/src/bunit/Rendering/Internal/Htmlizer.cs b/src/bunit/Rendering/Internal/Htmlizer.cs
index 9f31c4bd0..92b391c44 100644
--- a/src/bunit/Rendering/Internal/Htmlizer.cs
+++ b/src/bunit/Rendering/Internal/Htmlizer.cs
@@ -113,10 +113,8 @@ int position
case RenderTreeFrameType.ElementReferenceCapture:
case RenderTreeFrameType.ComponentReferenceCapture:
return position + 1;
-#if NET8_0_OR_GREATER
case RenderTreeFrameType.NamedEvent:
return position + 1;
-#endif
default:
throw new InvalidOperationException(
$"Invalid element frame type '{frame.FrameType}'."
diff --git a/src/bunit/Rendering/MissingRendererInfoException.cs b/src/bunit/Rendering/MissingRendererInfoException.cs
index a763a0d98..7eaf3c785 100644
--- a/src/bunit/Rendering/MissingRendererInfoException.cs
+++ b/src/bunit/Rendering/MissingRendererInfoException.cs
@@ -1,4 +1,3 @@
-#if NET9_0_OR_GREATER
namespace Bunit.Rendering;
///
@@ -11,11 +10,11 @@ public sealed class MissingRendererInfoException : Exception
///
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.
+ A component under test is trying to access the 'RendererInfo' property, which has not been specified. Set it via BunitContext.Renderer.SetRendererInfo.
For example:
- public class SomeTestClass : TestContext
+ public class SomeTestClass : BunitContext
{
[Fact]
public void SomeTestCase()
@@ -33,4 +32,3 @@ public void SomeTestCase()
HelpLink = "https://bunit.dev/docs/interaction/render-modes";
}
}
-#endif
diff --git a/src/bunit/Rendering/RenderModeMisMatchException.cs b/src/bunit/Rendering/RenderModeMisMatchException.cs
index 8187988e3..447c4e5d7 100644
--- a/src/bunit/Rendering/RenderModeMisMatchException.cs
+++ b/src/bunit/Rendering/RenderModeMisMatchException.cs
@@ -1,4 +1,3 @@
-#if NET9_0_OR_GREATER
namespace Bunit.Rendering;
///
@@ -19,4 +18,3 @@ Ensure that the render mode of the parent component matches the render mode of t
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/TestContext.cs b/src/bunit/TestContext.cs
deleted file mode 100644
index 2f3ad5022..000000000
--- a/src/bunit/TestContext.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Bunit;
-
-///
-[Obsolete($"Use {nameof(BunitContext)} instead. TestContext will be removed in a future release.", false, UrlFormat = "https://bunit.dev/docs/migrations")]
-public class TestContext : BunitContext
-{
-}
diff --git a/src/bunit/TestDoubles/Authorization/BunitAuthorizationExtensions.cs b/src/bunit/TestDoubles/Authorization/BunitAuthorizationExtensions.cs
index 8fad2d81d..c3dc37223 100644
--- a/src/bunit/TestDoubles/Authorization/BunitAuthorizationExtensions.cs
+++ b/src/bunit/TestDoubles/Authorization/BunitAuthorizationExtensions.cs
@@ -1,6 +1,5 @@
using Bunit.TestDoubles;
using Microsoft.AspNetCore.Components.Authorization;
-using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
namespace Bunit;
@@ -11,16 +10,9 @@ public partial class BunitContext
/// an authenticated user, as well as adding the component to the
/// test contexts render tree.
///
- [RemovedInFutureVersion("SignOutSessionStateManager should be removed from the container.")]
public BunitAuthorizationContext AddAuthorization()
{
Services.AddCascadingAuthenticationState();
-#if !NET11_0_OR_GREATER
- Services.AddSingleton();
-#pragma warning disable CS0618
- Services.AddSingleton(s => s.GetRequiredService());
-#pragma warning restore CS0618
-#endif
var authCtx = new BunitAuthorizationContext(Services);
return authCtx;
}
diff --git a/src/bunit/TestDoubles/NavigationManager/BunitSignOutSessionStateManager.cs b/src/bunit/TestDoubles/NavigationManager/BunitSignOutSessionStateManager.cs
deleted file mode 100644
index af6d78ecf..000000000
--- a/src/bunit/TestDoubles/NavigationManager/BunitSignOutSessionStateManager.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-#if !NET11_0_OR_GREATER
-using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
-
-namespace Bunit.TestDoubles;
-
-///
-/// Represents bUnit's own that captures calls to
-/// that will help later to assert if the user was logged out
-///
-#pragma warning disable CS0618
-[RemovedInFutureVersion("SignOutSessionStateManager is obsolete")]
-public class BunitSignOutSessionStateManager : SignOutSessionStateManager
-#pragma warning restore CS0618
-{
- ///
- /// Returns true when was called, otherwise false
- ///
- public bool IsSignedOut { get; set; }
-
- ///
- /// Initializes a new instance of
- ///
- public BunitSignOutSessionStateManager(IJSRuntime jsRuntime) : base(jsRuntime)
- {
- }
-
- ///
- public override ValueTask SetSignOutState()
- {
- IsSignedOut = true;
- return new ValueTask();
- }
-
- ///
- public override Task ValidateSignOutState()
- {
- var wasSignedOut = IsSignedOut;
- IsSignedOut = false;
- return Task.FromResult(wasSignedOut);
- }
-}
-#endif
diff --git a/src/bunit/bunit.csproj b/src/bunit/bunit.csproj
index 8712df6b9..14e3353c7 100644
--- a/src/bunit/bunit.csproj
+++ b/src/bunit/bunit.csproj
@@ -1,6 +1,6 @@
- net8.0;net9.0;net10.0;net11.0
+ net10.0;net11.0Bunit
diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj
index 28d889510..eb2e9fbcc 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;net9.0;net10.0;net11.0
+ net10.0;net11.0BunitBunit.Generator.Teststrue
diff --git a/tests/bunit.testassets/Assets/AssetsIndexerComponent.razor b/tests/bunit.testassets/Assets/AssetsIndexerComponent.razor
index 3b3a7cb3d..47e2c8b28 100644
--- a/tests/bunit.testassets/Assets/AssetsIndexerComponent.razor
+++ b/tests/bunit.testassets/Assets/AssetsIndexerComponent.razor
@@ -1,7 +1 @@
-@{
-#if NET9_0_OR_GREATER
-}
-@{
-#endif
-}
diff --git a/tests/bunit.testassets/Assets/AssetsIterationComponent.razor b/tests/bunit.testassets/Assets/AssetsIterationComponent.razor
index f6dfbe340..430c4a2b7 100644
--- a/tests/bunit.testassets/Assets/AssetsIterationComponent.razor
+++ b/tests/bunit.testassets/Assets/AssetsIterationComponent.razor
@@ -1,15 +1,8 @@
-@{
-#if NET9_0_OR_GREATER
-}
@foreach (var name in SubresourceNames)
{
@name
}
-@{
-#endif
-}
@code {
-#if NET9_0_OR_GREATER
private IReadOnlyList SubresourceNames { get; set; } = Array.Empty();
protected override void OnInitialized()
@@ -17,5 +10,4 @@
.Select(asset => asset.Properties?.SingleOrDefault(property => property.Name == "label")?.Value)
.OfType()
.ToList();
-#endif
}
diff --git a/tests/bunit.testassets/Assets/AssetsPropertiesComponent.razor b/tests/bunit.testassets/Assets/AssetsPropertiesComponent.razor
index bb4d4ffab..453e1e472 100644
--- a/tests/bunit.testassets/Assets/AssetsPropertiesComponent.razor
+++ b/tests/bunit.testassets/Assets/AssetsPropertiesComponent.razor
@@ -1,6 +1,3 @@
-@{
-#if NET9_0_OR_GREATER
-}
@foreach (var asset in Assets)
{
@@ -10,6 +7,3 @@
}
}
-@{
-#endif
-}
diff --git a/tests/bunit.testassets/DumpCapture.cs b/tests/bunit.testassets/DumpCapture.cs
index 822fddf28..ca635e590 100644
--- a/tests/bunit.testassets/DumpCapture.cs
+++ b/tests/bunit.testassets/DumpCapture.cs
@@ -49,11 +49,7 @@ public static async Task OnFailureAsync(
private static async Task CaptureDump(string testName, string testFilePath, ITestOutputHelper outputHelper)
{
-#if NETSTANDARD2_1
- var processId = Process.GetCurrentProcess().Id;
-#else
var processId = Environment.ProcessId;
-#endif
var dumpFilePath = Path.Combine(Directory.GetCurrentDirectory(), $"{Path.GetFileNameWithoutExtension(testFilePath)}-{testName}-wait-failed-{Guid.NewGuid()}.dmp");
// Attempt to start the dotnet-dump process
var startInfo = new ProcessStartInfo
@@ -72,11 +68,7 @@ private static async Task CaptureDump(string testName, string testFilePath, ITes
return;
}
-#if NETSTANDARD2_1
- process.WaitForExit();
-#else
await process.WaitForExitAsync();
-#endif
var output = await process.StandardOutput.ReadToEndAsync();
var error = await process.StandardError.ReadToEndAsync();
outputHelper.WriteLine($"Dump status: {{process.ExitCode}}. Dump file: {dumpFilePath}");
diff --git a/tests/bunit.testassets/RenderModes/ComponentThatPrintsAssignedRenderMode.razor b/tests/bunit.testassets/RenderModes/ComponentThatPrintsAssignedRenderMode.razor
index effe0ff2c..98e281912 100644
--- a/tests/bunit.testassets/RenderModes/ComponentThatPrintsAssignedRenderMode.razor
+++ b/tests/bunit.testassets/RenderModes/ComponentThatPrintsAssignedRenderMode.razor
@@ -1,9 +1 @@
-@{
-#if NET9_0_OR_GREATER
-}
-