diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 7b94efa69..ebcf504c5 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -17,11 +17,11 @@
"rollForward": false
},
"dotnet-dump": {
- "version": "8.0.532401",
+ "version": "8.0.547301",
"commands": [
"dotnet-dump"
],
"rollForward": false
}
}
-}
\ No newline at end of file
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e77d2e6e..8a852770f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,15 @@ All notable changes to **bUnit** will be documented in this file. The project ad
## [Unreleased]
+### Added
+
+- `bunit.generators` respect parameters from the base class.
+- Supports components using constructor injection in `net9.0`.
+
+### Fixed
+
+- Use latest `System.Text.Json` due to CVE in `8.0.4`.
+
## [1.32.7] - 2024-10-04
### Fixed
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 3726679b1..9ea352b21 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,8 +7,8 @@
-
-
+
+
@@ -20,7 +20,7 @@
-
+
@@ -28,6 +28,11 @@
+
+
+
+
+
@@ -40,9 +45,6 @@
-
-
-
@@ -57,9 +59,6 @@
-
-
-
@@ -73,9 +72,6 @@
-
-
-
@@ -89,9 +85,6 @@
-
-
-
@@ -105,9 +98,6 @@
-
-
-
@@ -121,8 +111,6 @@
-
-
diff --git a/src/bunit.core/Rendering/BunitComponentActivator.cs b/src/bunit.core/Rendering/BunitComponentActivator.cs
index e7afdd5c8..3d1e9c1de 100644
--- a/src/bunit.core/Rendering/BunitComponentActivator.cs
+++ b/src/bunit.core/Rendering/BunitComponentActivator.cs
@@ -7,10 +7,13 @@ internal class BunitComponentActivator : IComponentActivator
private readonly ComponentFactoryCollection factories;
private readonly IComponentActivator componentActivator;
- public BunitComponentActivator(ComponentFactoryCollection factories, IComponentActivator? externalComponentActivator)
+ public BunitComponentActivator(
+ IServiceProvider serviceProvider,
+ ComponentFactoryCollection factories,
+ IComponentActivator? externalComponentActivator)
{
this.factories = factories ?? throw new ArgumentNullException(nameof(factories));
- this.componentActivator = externalComponentActivator ?? DefaultComponentActivator.Instance;
+ this.componentActivator = externalComponentActivator ?? new DefaultComponentActivator(serviceProvider);
}
public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
@@ -43,12 +46,17 @@ public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessed
private sealed class DefaultComponentActivator : IComponentActivator
{
- public static IComponentActivator Instance { get; } = new DefaultComponentActivator();
+ private readonly IServiceProvider serviceProvider;
+
+ public DefaultComponentActivator(IServiceProvider serviceProvider)
+ {
+ this.serviceProvider = serviceProvider;
+ }
///
public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
- {
- return (IComponent)Activator.CreateInstance(componentType)!;
+ {
+ return (IComponent)ActivatorUtilities.CreateInstance(serviceProvider, componentType);
}
}
}
diff --git a/src/bunit.core/Rendering/TestRenderer.cs b/src/bunit.core/Rendering/TestRenderer.cs
index bf7449b4e..61d4deecf 100644
--- a/src/bunit.core/Rendering/TestRenderer.cs
+++ b/src/bunit.core/Rendering/TestRenderer.cs
@@ -79,7 +79,7 @@ public TestRenderer(IRenderedComponentActivator renderedComponentActivator, Test
/// Initializes a new instance of the class.
///
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory)
- : base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService(), null))
+ : base(services, loggerFactory, new BunitComponentActivator(services, services.GetRequiredService(), null))
{
logger = loggerFactory.CreateLogger();
this.activator = renderedComponentActivator;
@@ -89,7 +89,7 @@ public TestRenderer(IRenderedComponentActivator renderedComponentActivator, Test
/// Initializes a new instance of the class.
///
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory, IComponentActivator componentActivator)
- : base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService(), componentActivator))
+ : base(services, loggerFactory, new BunitComponentActivator(services, services.GetRequiredService(), componentActivator))
{
logger = loggerFactory.CreateLogger();
this.activator = renderedComponentActivator;
diff --git a/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs b/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs
index 6c7933786..c7fadff33 100644
--- a/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs
+++ b/src/bunit.generators/Web.Stubs/AddStubMethodStubGenerator/AddStubGenerator.cs
@@ -31,8 +31,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
private static AddStubClassInfo? GetStubClassInfo(GeneratorSyntaxContext context)
{
- var invocation = context.Node as InvocationExpressionSyntax;
- if (invocation is null || !IsComponentFactoryStubMethod(invocation, context.SemanticModel))
+ if (context.Node is not InvocationExpressionSyntax invocation || !IsComponentFactoryStubMethod(invocation, context.SemanticModel))
{
return null;
}
@@ -56,7 +55,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var line = lineSpan.StartLinePosition.Line + 1;
var column = lineSpan.Span.Start.Character + context.Node.ToString().IndexOf("AddStub", StringComparison.Ordinal) + 1;
- var properties = symbol.GetMembers()
+ var properties = symbol.GetAllMembersRecursively()
.OfType()
.Where(IsParameterOrCascadingParameter)
.Select(CreateFromProperty)
diff --git a/src/bunit.generators/Web.Stubs/AttributeStubGenerator/ComponentStubAttributeGenerator.cs b/src/bunit.generators/Web.Stubs/AttributeStubGenerator/ComponentStubAttributeGenerator.cs
index d36fcf184..ee778bb1f 100644
--- a/src/bunit.generators/Web.Stubs/AttributeStubGenerator/ComponentStubAttributeGenerator.cs
+++ b/src/bunit.generators/Web.Stubs/AttributeStubGenerator/ComponentStubAttributeGenerator.cs
@@ -62,7 +62,7 @@ private static bool IsClassWithComponentStubAttribute(SyntaxNode s) =>
}
var parameter = attribute.AttributeClass!.TypeArguments
- .SelectMany(s => s.GetMembers())
+ .SelectMany(s => s.GetAllMembersRecursively())
.OfType()
.Where(IsParameterOrCascadingParameter)
.Select(CreateFromProperty)
diff --git a/src/bunit.generators/Web.Stubs/MemberRetriever.cs b/src/bunit.generators/Web.Stubs/MemberRetriever.cs
new file mode 100644
index 000000000..00ce8950f
--- /dev/null
+++ b/src/bunit.generators/Web.Stubs/MemberRetriever.cs
@@ -0,0 +1,19 @@
+using Microsoft.CodeAnalysis;
+
+namespace Bunit.Web.Stubs;
+
+internal static class MemberRetriever
+{
+ public static IEnumerable GetAllMembersRecursively(this ITypeSymbol type)
+ {
+ var currentType = type;
+ while (currentType is not null)
+ {
+ foreach (var member in currentType.GetMembers())
+ {
+ yield return member;
+ }
+ currentType = currentType.BaseType;
+ }
+ }
+}
diff --git a/tests/bunit.generators.tests/Web.Stub/AddStubGeneratorTests.cs b/tests/bunit.generators.tests/Web.Stub/AddStubGeneratorTests.cs
index df54ed6f2..17c3dc44c 100644
--- a/tests/bunit.generators.tests/Web.Stub/AddStubGeneratorTests.cs
+++ b/tests/bunit.generators.tests/Web.Stub/AddStubGeneratorTests.cs
@@ -61,7 +61,42 @@ public void Generated_stub_via_attribute_has_same_parameters()
var stub = cut.FindComponent();
Assert.Equal("test", stub.Instance.Text);
}
+
+ [Fact]
+ public void Generated_Stub_respects_base_class_parameters()
+ {
+ ComponentFactories.AddStub();
+
+ var cut = RenderComponent(c => c.AddChildContent());
+
+ var stub = cut.FindComponent();
+ Assert.Equal(0, stub.Instance.BaseCount);
+ }
+
+ [Fact]
+ public void Generated_stub_via_attribute_respects_base_class_parameters()
+ {
+ ComponentFactories.Add();
+
+ var cut = RenderComponent(c => c.AddChildContent());
+
+ var stub = cut.FindComponent();
+ Assert.Equal(0, stub.Instance.BaseCount);
+ }
}
[ComponentStub]
-public partial class ButtonComponentStub;
\ No newline at end of file
+public partial class ButtonComponentStub;
+
+public abstract class BaseComponent : ComponentBase
+{
+ [Parameter] public int BaseCount { get; set; }
+}
+
+public class DerivedComponent : BaseComponent
+{
+ [Parameter] public int DerivedCount { get; set; }
+}
+
+[ComponentStub]
+public partial class DerivedComponentStubViaAttributeAnnotation;
\ No newline at end of file
diff --git a/tests/bunit.generators.tests/Web.Stub/Components/ContainerComponent.cs b/tests/bunit.generators.tests/Web.Stub/Components/ContainerComponent.cs
new file mode 100644
index 000000000..5cbbbb298
--- /dev/null
+++ b/tests/bunit.generators.tests/Web.Stub/Components/ContainerComponent.cs
@@ -0,0 +1,14 @@
+namespace Bunit.Web.Stub.Components;
+
+public class ContainerComponent : ComponentBase
+{
+ [Parameter]
+ public RenderFragment ChildContent { get; set; }
+
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ builder.OpenElement(0, "div");
+ builder.AddContent(1, ChildContent);
+ builder.CloseElement();
+ }
+}
diff --git a/tests/bunit.testassets/SampleComponents/ConstructorInjectionComponent.cs b/tests/bunit.testassets/SampleComponents/ConstructorInjectionComponent.cs
new file mode 100644
index 000000000..7b99ac4b1
--- /dev/null
+++ b/tests/bunit.testassets/SampleComponents/ConstructorInjectionComponent.cs
@@ -0,0 +1,13 @@
+using Microsoft.JSInterop;
+
+namespace Bunit.TestAssets.SampleComponents;
+
+public class ConstructorInjectionComponent : ComponentBase
+{
+ public IJSRuntime JSRuntime { get; }
+
+ public ConstructorInjectionComponent(IJSRuntime jsRuntime)
+ {
+ JSRuntime = jsRuntime;
+ }
+}
diff --git a/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs b/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs
index a4dd4a0d9..412779aad 100644
--- a/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs
+++ b/tests/bunit.web.tests/Rendering/RenderedComponentTest.cs
@@ -57,4 +57,14 @@ public void Test020()
Should.Throw(() => target.Instance);
}
+ #if NET9_0_OR_GREATER
+
+ [Fact(DisplayName = "Component with constructor dependencies is resolved when rendered")]
+ public void Test021()
+ {
+ var cut = RenderComponent();
+
+ cut.Instance.JSRuntime.ShouldNotBeNull();
+ }
+ #endif
}
diff --git a/version.json b/version.json
index 556cfe4ec..fc864ecdf 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.32",
+ "version": "1.33",
"assemblyVersion": {
"precision": "revision"
},