Skip to content

Commit 570b7a4

Browse files
authored
Merge pull request #1797 from bUnit-dev/release/v2.3
Release of new minor version v2.3
2 parents 45d3d74 + b1628eb commit 570b7a4

File tree

15 files changed

+472
-144
lines changed

15 files changed

+472
-144
lines changed

.github/workflows/prepare-release.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,20 @@ on:
55
workflow_dispatch:
66
inputs:
77
versionIncrement:
8-
description: 'The version increment. Allowed values are "major" and "minor".'
8+
description: 'The version increment. Allowed values are "major", "minor" and "build".'
9+
type: choice
910
required: true
11+
options:
12+
- major
13+
- minor
14+
- build
1015
default: 'minor'
1116

1217
jobs:
1318
prepare-release:
1419
name: 🚚 Prepare new release
1520
runs-on: ubuntu-latest
16-
if: github.ref == 'refs/heads/main' && contains(fromJson('["major","minor"]'), github.event.inputs.versionIncrement)
21+
if: github.ref == 'refs/heads/main' && contains(fromJson('["major","minor","build"]'), github.event.inputs.versionIncrement)
1722
steps:
1823
- name: 🛒 Checkout repository
1924
uses: actions/checkout@v6

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ All notable changes to **bUnit** will be documented in this file. The project ad
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Added generic overloads `Find{TComponent, TElement}` and `FindAll{TComponent, TElement}` to query for specific element types (e.g., `IHtmlInputElement`). By [@linkdotnet](https://github.com/linkdotnet).
12+
- Added generic overloads `WaitForElement{TComponent, TElement}` and `WaitForElements{TComponent, TElement}` to wait for specific element types. By [@linkdotnet](https://github.com/linkdotnet).
13+
14+
### Fixed
15+
16+
- Adding convenient overloads for `InputAsync` and `ChangeAsync` to have feature parity with the sync version. Reported by [@ScarletKuro](https://github.com/ScarletKuro). Fixed by [@linkdotnet](https://github.com/linkdotnet).
17+
918
## [2.2.2] - 2025-12-08
1019

1120
### Added

Directory.Packages.props

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,29 @@
2929
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
3030
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1"/>
3131
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3"/>
32-
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="8.0.21"/>
32+
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="8.0.22"/>
3333

34-
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.21"/>
35-
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.21"/>
34+
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.22"/>
35+
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.22"/>
3636
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1"/>
37-
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="8.0.21"/>
38-
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.21"/>
39-
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.21"/>
37+
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="8.0.22"/>
38+
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.22"/>
39+
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.22"/>
4040

4141
<PackageVersion Include="System.Text.Json" Version="8.0.5"/>
4242
</ItemGroup>
4343

4444
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
45-
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.10"/>
46-
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10"/>
47-
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.10"/>
45+
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.11"/>
46+
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.11"/>
47+
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.11"/>
4848

49-
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.10"/>
50-
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.10"/>
51-
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.10"/>
52-
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.10"/>
53-
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.10"/>
54-
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.10"/>
49+
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.11"/>
50+
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.11"/>
51+
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.11"/>
52+
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.11"/>
53+
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.11"/>
54+
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.11"/>
5555

5656
<PackageVersion Include="System.Text.Json" Version="9.0.9"/>
5757
</ItemGroup>

MIGRATION.md

Lines changed: 0 additions & 107 deletions
This file was deleted.

bunit.slnx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
</Folder>
1212
<Folder Name="/.text/">
1313
<File Path="CHANGELOG.md" />
14-
<File Path="MIGRATION.md" />
1514
<File Path="README.md" />
1615
</Folder>
1716
<Folder Name="/.workflows/">

docs/site/docs/migrations/1to2.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,38 @@ cut.Find("button").Click(detail: 2, ctrlKey: true);
7878

7979
The last one was a method with all parameters of `MouseEventArgs` as optional parameters. This method has been removed in favor of using the `MouseEventArgs` directly.
8080

81-
Also `ClickAsync` - to align with its synchronous counterpart - doesn't take `MouseEventArgs` as mandatory parameter anymore. If not set, a default instance will be created.
81+
Also `ClickAsync` - to align with its synchronous counterpart - doesn't take `MouseEventArgs` as mandatory parameter anymore. If not set, a default instance will be created.
82+
83+
## `DisposeComponents` is now async and called `DisposeComponentsAsync`
84+
85+
The `DisposeComponents` method has been renamed to `DisposeComponentsAsync` and is now asynchronous. To migrate, simply rename the method and add `await`:
86+
87+
```diff
88+
- DisposeComponents();
89+
+ await DisposeComponentsAsync();
90+
```
91+
92+
## The `ComponentParameterFactory` and `ComponentParameter` has been removed
93+
The `ComponentParameterFactory` class has been removed (and therefore the usage of `ComponentParameter`).
94+
Instead, use the `Render` method (and its overloads) to pass parameters to components.
95+
96+
## `IRefreshableElementCollection` was removed
97+
The `IRefreshableElementCollection` interface has been removed. With that, the overload in `FindAll` doesn't accept a `bool refresh` parameter anymore. Instead, simply call `FindAll` again to get a refreshed collection.
98+
99+
```csharp
100+
var items = cut.FindAll("li", refresh: true);
101+
items.Count.ShouldBe(3);
102+
cut.Find("button").Click(); // This changes the list items
103+
104+
items.Count.ShouldBe(4);
105+
```
106+
107+
Should be changed to:
108+
109+
```csharp
110+
var items = cut.FindAll("li");
111+
items.Count.ShouldBe(3);
112+
cut.Find("button").Click(); // This changes the list items
113+
items = cut.FindAll("li"); // Call FindAll again to refresh
114+
items.Count.ShouldBe(4);
115+
```

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<PropertyGroup Label="Package Validation" Condition="$(MSBuildProjectName) != 'bunit.generators'">
2727
<EnablePackageValidation>true</EnablePackageValidation>
2828
<GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile>
29-
<PackageValidationBaselineVersion>1.25.3</PackageValidationBaselineVersion>
29+
<PackageValidationBaselineVersion>2.0.66</PackageValidationBaselineVersion>
3030
</PropertyGroup>
3131

3232
<PropertyGroup Label="NuGet package information">

src/bunit/EventDispatchExtensions/InputEventDispatchExtensions.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Bunit;
99
public static partial class EventHandlerDispatchExtensions
1010
{
1111
/// <summary>
12-
/// Raises the <c>@onchange</c> event on <paramref name="element"/>, passing the provided
12+
/// Raises the <c>@onchange</c> event on <paramref name="element"/>, passing the provided
1313
/// properties to the event handler via a <see cref="ChangeEventArgs"/> object.
1414
/// </summary>
1515
/// <param name="element">The element to raise the event on.</param>
@@ -18,14 +18,32 @@ public static void Change<T>(this IElement element, T value)
1818
=> _ = ChangeAsync(element, CreateFrom(value));
1919

2020
/// <summary>
21-
/// Raises the <c>@oninput</c> event on <paramref name="element"/>, passing the provided
21+
/// Raises the <c>@onchange</c> event on <paramref name="element"/>, passing the provided
22+
/// properties to the event handler via a <see cref="ChangeEventArgs"/> object.
23+
/// </summary>
24+
/// <param name="element">The element to raise the event on.</param>
25+
/// <param name="value">The new value.</param>
26+
public static void ChangeAsync<T>(this IElement element, T value)
27+
=> _ = ChangeAsync(element, CreateFrom(value));
28+
29+
/// <summary>
30+
/// Raises the <c>@oninput</c> event on <paramref name="element"/>, passing the provided
2231
/// properties to the event handler via a <see cref="ChangeEventArgs"/> object.
2332
/// </summary>
2433
/// <param name="element">The element to raise the event on.</param>
2534
/// <param name="value">The new value.</param>
2635
public static void Input<T>(this IElement element, T value)
2736
=> _ = InputAsync(element, CreateFrom(value));
2837

38+
/// <summary>
39+
/// Raises the <c>@oninput</c> event on <paramref name="element"/>, passing the provided
40+
/// properties to the event handler via a <see cref="ChangeEventArgs"/> object.
41+
/// </summary>
42+
/// <param name="element">The element to raise the event on.</param>
43+
/// <param name="value">The new value.</param>
44+
public static void InputAsync<T>(this IElement element, T value)
45+
=> _ = InputAsync(element, CreateFrom(value));
46+
2947
private static ChangeEventArgs CreateFrom<T>(T value) => new() { Value = FormatValue(value) };
3048

3149
private static object? FormatValue<T>(T value)

src/bunit/Extensions/RenderedComponentExtensions.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,21 @@ public static class RenderedComponentExtensions
1818
/// <param name="cssSelector">The group of selectors to use.</param>
1919
public static IElement Find<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
2020
where TComponent : IComponent
21+
=> Find<TComponent, IElement>(renderedComponent, cssSelector);
22+
23+
/// <summary>
24+
/// Returns the first element of type <typeparamref name="TElement"/> from the rendered fragment or component under test,
25+
/// using the provided <paramref name="cssSelector"/>, in a depth-first pre-order traversal
26+
/// of the rendered nodes.
27+
/// </summary>
28+
/// <typeparam name="TComponent">The type of the component under test.</typeparam>
29+
/// <typeparam name="TElement">The type of element to find (e.g., IHtmlInputElement).</typeparam>
30+
/// <param name="renderedComponent">The rendered fragment to search.</param>
31+
/// <param name="cssSelector">The group of selectors to use.</param>
32+
/// <exception cref="ElementNotFoundException">Thrown if no element matches the <paramref name="cssSelector"/>.</exception>
33+
public static TElement Find<TComponent, TElement>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
34+
where TComponent : IComponent
35+
where TElement : class, IElement
2136
{
2237
ArgumentNullException.ThrowIfNull(renderedComponent);
2338

@@ -26,7 +41,11 @@ public static IElement Find<TComponent>(this IRenderedComponent<TComponent> rend
2641
if (result is null)
2742
throw new ElementNotFoundException(cssSelector);
2843

29-
return result.WrapUsing(new CssSelectorElementFactory((IRenderedComponent<IComponent>)renderedComponent, cssSelector));
44+
if (result is not TElement)
45+
throw new ElementNotFoundException(
46+
$"The element matching '{cssSelector}' is of type '{result.GetType().Name}', not '{typeof(TElement).Name}'.");
47+
48+
return (TElement)result.WrapUsing(new CssSelectorElementFactory((IRenderedComponent<IComponent>)renderedComponent, cssSelector));
3049
}
3150

3251
/// <summary>
@@ -39,10 +58,25 @@ public static IElement Find<TComponent>(this IRenderedComponent<TComponent> rend
3958
/// <returns>An <see cref="IReadOnlyList{IElement}"/>, that can be refreshed to execute the search again.</returns>
4059
public static IReadOnlyList<IElement> FindAll<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
4160
where TComponent : IComponent
61+
=> FindAll<TComponent, IElement>(renderedComponent, cssSelector);
62+
63+
/// <summary>
64+
/// Returns a collection of elements of type <typeparamref name="TElement"/> from the rendered fragment or component under test,
65+
/// using the provided <paramref name="cssSelector"/>, in a depth-first pre-order traversal
66+
/// of the rendered nodes. Only elements matching the type <typeparamref name="TElement"/> are returned.
67+
/// </summary>
68+
/// <typeparam name="TComponent">The type of the component under test.</typeparam>
69+
/// <typeparam name="TElement">The type of elements to find (e.g., IHtmlInputElement).</typeparam>
70+
/// <param name="renderedComponent">The rendered fragment to search.</param>
71+
/// <param name="cssSelector">The group of selectors to use.</param>
72+
/// <returns>An <see cref="IReadOnlyList{TElement}"/> containing only elements matching the specified type.</returns>
73+
public static IReadOnlyList<TElement> FindAll<TComponent, TElement>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
74+
where TComponent : IComponent
75+
where TElement : class, IElement
4276
{
4377
ArgumentNullException.ThrowIfNull(renderedComponent);
4478

45-
return renderedComponent.Nodes.QuerySelectorAll(cssSelector).ToArray();
79+
return renderedComponent.Nodes.QuerySelectorAll(cssSelector).OfType<TElement>().ToArray();
4680
}
4781

4882
/// <summary>

0 commit comments

Comments
 (0)