Skip to content

Commit

Permalink
Merge pull request #1140 from bUnit-dev/release/v1.21
Browse files Browse the repository at this point in the history
Release of new minor version v1.21
  • Loading branch information
egil authored Jul 2, 2023
2 parents 57be80e + 887f574 commit b24a8cc
Show file tree
Hide file tree
Showing 34 changed files with 561 additions and 413 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-serve": {
"version": "1.10.155",
"version": "1.10.172",
"commands": [
"dotnet-serve"
]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ jobs:
- name: ⏭ Create pull request from stable to main when direct merge fails
if: steps.mergeMainline.outcome == 'failure'
uses: thomaseizinger/[email protected].0
uses: thomaseizinger/[email protected].1
env:
GITHUB_TOKEN: ${{ secrets.BUNIT_BOT_TOKEN }}
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
run: git push origin release/v$NBGV_MajorMinorVersion

- name: ⏭ Create pull request for release branch
uses: thomaseizinger/[email protected].0
uses: thomaseizinger/[email protected].1
env:
GITHUB_TOKEN: ${{ secrets.BUNIT_BOT_TOKEN }}
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ jobs:
- name: ⏭ Create pull request from stable to main when direct merge fails
if: steps.mergeMainline.outcome == 'failure'
uses: thomaseizinger/[email protected].0
uses: thomaseizinger/[email protected].1
env:
GITHUB_TOKEN: ${{ secrets.BUNIT_BOT_TOKEN }}
with:
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/verification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ concurrency:
group: verification-${{ github.ref }}-1
cancel-in-progress: true

env:
VSTEST_CONNECTION_TIMEOUT: 180
DOTNET_NOLOGO: "true"

jobs:
verify-bunit:
name: 👌 Verify bUnit
Expand Down Expand Up @@ -112,4 +116,4 @@ jobs:
- name: 📄 Building docs
run: |
dotnet tool update -g docfx --version 2.67.0
docfx docs/site/docfx.json
docfx docs/site/docfx.json
392 changes: 199 additions & 193 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
<!-- Shared code analyzers used for all projects in the solution -->
<ItemGroup Label="Code Analyzers">
<PackageReference Include="AsyncFixer" Version="1.6.0" PrivateAssets="All" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.0.0.68202" PrivateAssets="All" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.4.0.72892" PrivateAssets="All" />
</ItemGroup>

<ItemGroup Label="Implicit usings"
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.132" PrivateAssets="All" />
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="All" />

</ItemGroup>

Expand Down
1 change: 0 additions & 1 deletion src/bunit.core/ComponentFactoryCollection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#if NET5_0_OR_GREATER
using System.Collections;
using System.Collections.Generic;

namespace Bunit;

Expand Down
3 changes: 1 addition & 2 deletions src/bunit.core/ComponentParameterCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ public void Add(IEnumerable<ComponentParameter> parameters)
/// the parameters in the collection passed to it.
/// </summary>
/// <typeparam name="TComponent">Type of component to render.</typeparam>
[SuppressMessage("Design", "MA0051:Method is too long", Justification = "TODO: Refactor")]
public RenderFragment ToRenderFragment<TComponent>()
where TComponent : IComponent
{
Expand Down Expand Up @@ -118,7 +117,7 @@ void AddAttributes(RenderTreeBuilder builder)
foreach (var pgroup in parameters.Where(x => !x.IsCascadingValue).GroupBy(x => x.Name, StringComparer.Ordinal))
{
var group = pgroup.ToArray();
var groupObject = group.FirstOrDefault(x => x.Value is not null).Value;
var groupObject = Array.Find(group, x => x.Value is not null).Value;

if (group.Length == 1)
{
Expand Down
4 changes: 3 additions & 1 deletion src/bunit.core/ComponentParameterCollectionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,9 @@ private static bool HasPublicParameterProperty(string parameterName)
var type = typeof(TComponent);
var property = type.GetProperty(parameterName);

return property != null && property.GetCustomAttributes(inherit: true).Any(a => a is ParameterAttribute);
return property is not null && Array.Exists(
property.GetCustomAttributes(inherit: true),
a => a is ParameterAttribute);
}

private static bool IsConcreteGenericOf(Type type, Type openGeneric)
Expand Down
21 changes: 3 additions & 18 deletions src/bunit.core/Extensions/RenderedComponentRenderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Bunit.Rendering;
using System.Runtime.ExceptionServices;

namespace Bunit;
Expand Down Expand Up @@ -28,24 +29,8 @@ public static void SetParametersAndRender<TComponent>(this IRenderedComponentBas
if (renderedComponent is null)
throw new ArgumentNullException(nameof(renderedComponent));

var result = renderedComponent.InvokeAsync(() =>
renderedComponent.Instance.SetParametersAsync(parameters));

if (result.IsFaulted && result.Exception is not null)
{
if (result.Exception.InnerExceptions.Count == 1)
{
ExceptionDispatchInfo.Capture(result.Exception.InnerExceptions[0]).Throw();
}
else
{
ExceptionDispatchInfo.Capture(result.Exception).Throw();
}
}
else if (!result.IsCompleted)
{
result.GetAwaiter().GetResult();
}
var renderer = renderedComponent.Services.GetRequiredService<TestRenderer>();
renderer.SetDirectParameters(renderedComponent, parameters);
}

/// <summary>
Expand Down
20 changes: 15 additions & 5 deletions src/bunit.core/Rendering/BunitComponentActivator.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
#if NET5_0_OR_GREATER
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;

namespace Bunit.Rendering;

internal class BunitComponentActivator : IComponentActivator
{
private readonly ComponentFactoryCollection factories;
private readonly IComponentActivator componentActivator;

public BunitComponentActivator(ComponentFactoryCollection factories)
public BunitComponentActivator(ComponentFactoryCollection factories, IComponentActivator? externalComponentActivator)
{
this.factories = factories ?? throw new ArgumentNullException(nameof(factories));
this.componentActivator = externalComponentActivator ?? DefaultComponentActivator.Instance;
}

public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
Expand Down Expand Up @@ -39,7 +38,18 @@ public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessed
}
}

return (IComponent)Activator.CreateInstance(componentType)!;
return componentActivator.CreateInstance(componentType);
}

private sealed class DefaultComponentActivator : IComponentActivator
{
public static IComponentActivator Instance { get; } = new DefaultComponentActivator();

/// <inheritdoc />
public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
{
return (IComponent)Activator.CreateInstance(componentType)!;
}
}
}
#endif
4 changes: 2 additions & 2 deletions src/bunit.core/Rendering/ITestRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Task DispatchEventAsync(
/// <summary>
/// Renders a <typeparamref name="TComponent"/> with the <paramref name="parameters"/> passed to it.
/// </summary>
/// <typeparam name = "TComponent" > The type of component to render.</typeparam>
/// <typeparam name="TComponent">The type of component to render.</typeparam>
/// <param name="parameters">The parameters to pass to the component.</param>
/// <returns>A <see cref="IRenderedComponentBase{TComponent}"/> that provides access to the rendered component.</returns>
IRenderedComponentBase<TComponent> RenderComponent<TComponent>(ComponentParameterCollection parameters)
Expand All @@ -77,5 +77,5 @@ IReadOnlyList<IRenderedComponentBase<TComponent>> FindComponents<TComponent>(IRe
/// <summary>
/// Disposes all components rendered by the <see cref="ITestRenderer" />.
/// </summary>
public void DisposeComponents();
void DisposeComponents();
}
2 changes: 1 addition & 1 deletion src/bunit.core/Rendering/RootRenderTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public bool TryAdd<TComponent>(Action<ComponentParameterCollectionBuilder<TCompo
where TComponent : IComponent
{
var componentType = typeof(TComponent);
if (registrations.Any(x => x.ComponentType == componentType))
if (registrations.Exists(x => x.ComponentType == componentType))
return false;

var registration = new RootRenderTreeRegistration(componentType, CreateRenderFragmentBuilder(parameterBuilder));
Expand Down
66 changes: 62 additions & 4 deletions src/bunit.core/Rendering/TestRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Reflection;
using System.Runtime.ExceptionServices;
using Microsoft.Extensions.Logging;

Expand All @@ -6,8 +7,13 @@ namespace Bunit.Rendering;
/// <summary>
/// Represents a bUnit <see cref="ITestRenderer"/> used to render Blazor components and fragments during bUnit tests.
/// </summary>
[SuppressMessage("Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", Justification = "Blazors internal API has been stable for a while now.")]
public class TestRenderer : Renderer, ITestRenderer
{
private static readonly Type RendererType = typeof(Renderer);
private static readonly FieldInfo IsBatchInProgressField = RendererType.GetField("_isBatchInProgress", BindingFlags.Instance | BindingFlags.NonPublic)!;
private static readonly MethodInfo GetRequiredComponentStateMethod = RendererType.GetMethod("GetRequiredComponentState", BindingFlags.Instance | BindingFlags.NonPublic)!;

private readonly object renderTreeUpdateLock = new();
private readonly SynchronizationContext? usersSyncContext = SynchronizationContext.Current;
private readonly Dictionary<int, IRenderedFragmentBase> renderedComponents = new();
Expand All @@ -18,6 +24,14 @@ public class TestRenderer : Renderer, ITestRenderer
private TaskCompletionSource<Exception> unhandledExceptionTsc = new(TaskCreationOptions.RunContinuationsAsynchronously);
private Exception? capturedUnhandledException;

private bool IsBatchInProgress
{
#pragma warning disable S1144 // Unused private types or members should be removed
get => (bool)(IsBatchInProgressField.GetValue(this) ?? false);
#pragma warning restore S1144 // Unused private types or members should be removed
set => IsBatchInProgressField.SetValue(this, value);
}

/// <inheritdoc/>
public Task<Exception> UnhandledException => unhandledExceptionTsc.Task;

Expand All @@ -29,6 +43,7 @@ public class TestRenderer : Renderer, ITestRenderer
/// </summary>
internal int RenderCount { get; private set; }

#if NETSTANDARD
/// <summary>
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
/// </summary>
Expand All @@ -38,13 +53,22 @@ public TestRenderer(IRenderedComponentActivator renderedComponentActivator, Test
logger = loggerFactory.CreateLogger<TestRenderer>();
this.activator = renderedComponentActivator;
}
#elif NET5_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
/// </summary>
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory)
: base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService<ComponentFactoryCollection>(), null))
{
logger = loggerFactory.CreateLogger<TestRenderer>();
this.activator = renderedComponentActivator;
}

#if NET5_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
/// </summary>
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory, IComponentActivator componentActivator)
: base(services, loggerFactory, componentActivator)
: base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService<ComponentFactoryCollection>(), componentActivator))
{
logger = loggerFactory.CreateLogger<TestRenderer>();
this.activator = renderedComponentActivator;
Expand Down Expand Up @@ -164,6 +188,34 @@ public void DisposeComponents()
AssertNoUnhandledExceptions();
}

/// <inheritdoc/>
internal void SetDirectParameters(IRenderedFragmentBase renderedComponent, ParameterView parameters)
{
Dispatcher.InvokeAsync(() =>
{
try
{
IsBatchInProgress = true;

var componentState = GetRequiredComponentStateMethod.Invoke(this, new object[] { renderedComponent.ComponentId })!;
var setDirectParametersMethod = componentState.GetType().GetMethod("SetDirectParameters", BindingFlags.Public | BindingFlags.Instance)!;
setDirectParametersMethod.Invoke(componentState, new object[] { parameters });
}
catch (TargetInvocationException ex) when (ex.InnerException is not null)
{
HandleException(ex.InnerException);
}
finally
{
IsBatchInProgress = false;
}

ProcessPendingRender();
});

AssertNoUnhandledExceptions();
}

/// <inheritdoc/>
protected override void ProcessPendingRender()
{
Expand Down Expand Up @@ -237,6 +289,11 @@ private void UpdateDisplay(in RenderBatch renderBatch)
{
var id = renderBatch.DisposedComponentIDs.Array[i];

// Add disposed components to the frames collection
// to avoid them being added into the dictionary later during a
// LoadRenderTreeFrames/GetOrLoadRenderTreeFrame call.
renderEvent.Frames.Add(id, default);

logger.LogComponentDisposed(id);

if (renderedComponents.TryGetValue(id, out var rc))
Expand Down Expand Up @@ -375,7 +432,7 @@ void FindComponentsInRenderTree(int componentId)
}
}

IRenderedComponentBase<TComponent> GetOrCreateRenderedComponent<TComponent>(RenderTreeFrameDictionary framesCollection, int componentId, TComponent component)
private IRenderedComponentBase<TComponent> GetOrCreateRenderedComponent<TComponent>(RenderTreeFrameDictionary framesCollection, int componentId, TComponent component)
where TComponent : IComponent
{
if (renderedComponents.TryGetValue(componentId, out var renderedComponent))
Expand All @@ -401,7 +458,8 @@ private void LoadRenderTreeFrames(int componentId, RenderTreeFrameDictionary fra
for (var i = 0; i < frames.Count; i++)
{
ref var frame = ref frames.Array[i];
if (frame.FrameType == RenderTreeFrameType.Component)

if (frame.FrameType == RenderTreeFrameType.Component && !framesCollection.Contains(frame.ComponentId))
{
LoadRenderTreeFrames(frame.ComponentId, framesCollection);
}
Expand Down
2 changes: 1 addition & 1 deletion src/bunit.core/TestContextBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected TestContextBase()
{
Services = new TestServiceProvider();
#if NET5_0_OR_GREATER
Services.AddSingleton<IComponentActivator>(new BunitComponentActivator(ComponentFactories));
Services.AddSingleton<ComponentFactoryCollection>(_ => ComponentFactories);
#endif
}

Expand Down
2 changes: 1 addition & 1 deletion src/bunit.web/Asserting/DiffAssertExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static IDiff ShouldHaveSingleChange(this IEnumerable<IDiff> diffs)
expectedText: "Expected changes",
message: "There were more than one change");

return diffsArray.First();
return diffsArray[0];
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions src/bunit.web/Extensions/TestServiceProviderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static IServiceCollection AddDefaultTestContextServices(this IServiceColl
// bUnit specific services
services.AddSingleton<TestContextBase>(testContext);
services.AddSingleton<WebTestRenderer>();
services.AddSingleton<TestRenderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddSingleton<Renderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddSingleton<ITestRenderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddSingleton<HtmlComparer>();
Expand Down
Loading

0 comments on commit b24a8cc

Please sign in to comment.