Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to **bUnit** will be documented in this file. The project ad

## Added
- Added `FindByAllByLabel` to `bunit.web.query` package. By [@linkdotnet](https://github.com/linkdotnet).
- 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).
- Added generic overloads `WaitForElement<TComponent, TElement>` and `WaitForElements<TComponent, TElement>` to wait for specific element types. By [@linkdotnet](https://github.com/linkdotnet).

## [2.1.1] - 2025-11-21

Expand Down
38 changes: 36 additions & 2 deletions src/bunit/Extensions/RenderedComponentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ public static class RenderedComponentExtensions
/// <param name="cssSelector">The group of selectors to use.</param>
public static IElement Find<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
where TComponent : IComponent
=> Find<TComponent, IElement>(renderedComponent, cssSelector);

/// <summary>
/// Returns the first element of type <typeparamref name="TElement"/> from the rendered fragment or component under test,
/// using the provided <paramref name="cssSelector"/>, in a depth-first pre-order traversal
/// of the rendered nodes.
/// </summary>
/// <typeparam name="TComponent">The type of the component under test.</typeparam>
/// <typeparam name="TElement">The type of element to find (e.g., IHtmlInputElement).</typeparam>
/// <param name="renderedComponent">The rendered fragment to search.</param>
/// <param name="cssSelector">The group of selectors to use.</param>
/// <exception cref="ElementNotFoundException">Thrown if no element matches the <paramref name="cssSelector"/>.</exception>
public static TElement Find<TComponent, TElement>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
where TComponent : IComponent
where TElement : class, IElement
{
ArgumentNullException.ThrowIfNull(renderedComponent);

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

return result.WrapUsing(new CssSelectorElementFactory((IRenderedComponent<IComponent>)renderedComponent, cssSelector));
if (result is not TElement)
throw new ElementNotFoundException(
$"The element matching '{cssSelector}' is of type '{result.GetType().Name}', not '{typeof(TElement).Name}'.");

return (TElement)result.WrapUsing(new CssSelectorElementFactory((IRenderedComponent<IComponent>)renderedComponent, cssSelector));
}

/// <summary>
Expand All @@ -39,10 +58,25 @@ public static IElement Find<TComponent>(this IRenderedComponent<TComponent> rend
/// <returns>An <see cref="IReadOnlyList{IElement}"/>, that can be refreshed to execute the search again.</returns>
public static IReadOnlyList<IElement> FindAll<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
where TComponent : IComponent
=> FindAll<TComponent, IElement>(renderedComponent, cssSelector);

/// <summary>
/// Returns a collection of elements of type <typeparamref name="TElement"/> from the rendered fragment or component under test,
/// using the provided <paramref name="cssSelector"/>, in a depth-first pre-order traversal
/// of the rendered nodes. Only elements matching the type <typeparamref name="TElement"/> are returned.
/// </summary>
/// <typeparam name="TComponent">The type of the component under test.</typeparam>
/// <typeparam name="TElement">The type of elements to find (e.g., IHtmlInputElement).</typeparam>
/// <param name="renderedComponent">The rendered fragment to search.</param>
/// <param name="cssSelector">The group of selectors to use.</param>
/// <returns>An <see cref="IReadOnlyList{TElement}"/> containing only elements matching the specified type.</returns>
public static IReadOnlyList<TElement> FindAll<TComponent, TElement>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
where TComponent : IComponent
where TElement : class, IElement
{
ArgumentNullException.ThrowIfNull(renderedComponent);

return renderedComponent.Nodes.QuerySelectorAll(cssSelector).ToArray();
return renderedComponent.Nodes.QuerySelectorAll(cssSelector).OfType<TElement>().ToArray();
}

/// <summary>
Expand Down
Loading