Skip to content
Merged
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
23 changes: 23 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,26 @@ The `TestServiceProvider` class has been renamed to `BunitTestServiceProvider`.

## `DisposeComponents` is now asynchronous and called `DisposeComponentsAsync`
`DisposeComponentsAsync` allows to await `DisposeAsync` of components under test. If you used `DisposeComponents`, you should replace it with `DisposeComponentsAsync`.

## `IRefreshableElementCollection` was removed

The `IRefreshableElementCollection` interface has been removed. With this the `FindAll` method does not accept a `bool enableRefresh` parameter anymore. Code like this:

```csharp
var items = cut.FindAll("li", enableRefresh: true);

cut.Find("button").Click(); // Some action that causes items to change

Assert.Equal(3, items.Count);
```

Should be changed to this:

```csharp
var items = cut.FindAll("li");

cut.Find("button").Click(); // Some action that causes items to change

items = cut.FindAll("li"); // Re-query the items
Assert.Equal(3, items.Count);
```
8 changes: 2 additions & 6 deletions docs/site/docs/verification/verify-markup.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ bUnit supports multiple different ways of searching and querying the rendered HT

- `FindByLabelText(string labelText)` that takes a text string used to label an input element and returns an `IElement` as output, or throws an exception if none are found (this is included in the experimental library [bunit.web.query](https://www.nuget.org/packages/bunit.web.query)). Use this method when possible compared to the generic `Find` and `FindAll` methods.
- [`Find(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.Find``1(Bunit.IRenderedComponent{``0},System.String)) takes a "CSS selector" as input and returns an `IElement` as output, or throws an exception if none are found.
- [`FindAll(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.FindAll``1(Bunit.IRenderedComponent{``0},System.String,System.Boolean)) takes a "CSS selector" as input and returns a list of `IElement` elements.
- [`FindAll(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.FindAll``1(Bunit.IRenderedComponent{``0},System.String)) takes a "CSS selector" as input and returns a list of `IElement` elements.

Let's see some examples of using the [`Find(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.Find``1(Bunit.IRenderedComponent{``0},System.String)) and [`FindAll(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.FindAll``1(Bunit.IRenderedComponent{``0},System.String,System.Boolean)) methods to query the `<FancyTable>` component listed below.
Let's see some examples of using the [`Find(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.Find``1(Bunit.IRenderedComponent{``0},System.String)) and [`FindAll(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.FindAll``1(Bunit.IRenderedComponent{``0},System.String)) methods to query the `<FancyTable>` component listed below.

[!code-razor[FancyTable.razor](../../../samples/components/FancyTable.razor)]

Expand All @@ -59,10 +59,6 @@ However, that does not apply to elements that are found by traversing the DOM tr

As a result of this, it is always recommended to use the [`Find(string cssSelector)`](xref:Bunit.RenderedComponentExtensions.Find``1(Bunit.IRenderedComponent{``0},System.String)) method when searching for a single element. Alternatively, always reissue the query whenever you need the element.

#### Auto-refreshable FindAll() queries

The [`FindAll(string cssSelector, bool enableAutoRefresh = false)`](xref:Bunit.RenderedComponentExtensions.FindAll``1(Bunit.IRenderedComponent{``0},System.String,System.Boolean)) method has an optional parameter, `enableAutoRefresh`, which when set to `true` will return a collection of `IElement`. This automatically refreshes itself when the component the elements came from is re-rendered.

## Semantic comparison of markup

Working with raw markup only works well with very simple output, but even then you have to sanitize it to get stable tests. A much better approach is to use the semantic HTML comparer that comes with bUnit.
Expand Down
24 changes: 0 additions & 24 deletions src/bunit/Extensions/IRefreshableElementCollection.cs

This file was deleted.

59 changes: 0 additions & 59 deletions src/bunit/Extensions/Internal/RefreshableElementCollection.cs

This file was deleted.

8 changes: 4 additions & 4 deletions src/bunit/Extensions/RenderedComponentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ public static IElement Find<TComponent>(this IRenderedComponent<TComponent> rend
/// </summary>
/// <param name="renderedComponent">The rendered fragment to search.</param>
/// <param name="cssSelector">The group of selectors to use.</param>
/// <param name="enableAutoRefresh">If true, the returned <see cref="IRefreshableElementCollection{IElement}"/> will automatically refresh its <see cref="IElement"/>s whenever the <paramref name="renderedComponent"/> changes.</param>
/// <returns>An <see cref="IRefreshableElementCollection{IElement}"/>, that can be refreshed to execute the search again.</returns>
public static IRefreshableElementCollection<IElement> FindAll<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, bool enableAutoRefresh = false)
/// <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
{
ArgumentNullException.ThrowIfNull(renderedComponent);
return new RefreshableElementCollection((IRenderedComponent<IComponent>)renderedComponent, cssSelector) { EnableAutoRefresh = enableAutoRefresh };

return renderedComponent.Nodes.QuerySelectorAll(cssSelector).ToArray();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public static IElement WaitForElement<TComponent>(this IRenderedComponent<TCompo
/// <param name="renderedComponent">The render fragment or component find the matching element in.</param>
/// <param name="cssSelector">The CSS selector to use to search for elements.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
public static IRefreshableElementCollection<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
public static IReadOnlyList<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
where TComponent : IComponent => WaitForElementsCore(renderedComponent, cssSelector, matchElementCount: null, timeout: null);

/// <summary>
Expand All @@ -52,8 +52,8 @@ public static IRefreshableElementCollection<IElement> WaitForElements<TComponent
/// <param name="cssSelector">The CSS selector to use to search for elements.</param>
/// <param name="matchElementCount">The exact number of elements to that the <paramref name="cssSelector"/> should match.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
public static IRefreshableElementCollection<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
public static IReadOnlyList<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount)
where TComponent : IComponent => WaitForElementsCore(renderedComponent, cssSelector, matchElementCount: matchElementCount, timeout: null);

/// <summary>
Expand All @@ -64,8 +64,8 @@ public static IRefreshableElementCollection<IElement> WaitForElements<TComponent
/// <param name="cssSelector">The CSS selector to use to search for elements.</param>
/// <param name="timeout">The maximum time to wait for elements to appear.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
public static IRefreshableElementCollection<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, TimeSpan timeout)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
public static IReadOnlyList<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, TimeSpan timeout)
where TComponent : IComponent
=> WaitForElementsCore(renderedComponent, cssSelector, matchElementCount: null, timeout: timeout);

Expand All @@ -78,8 +78,8 @@ public static IRefreshableElementCollection<IElement> WaitForElements<TComponent
/// <param name="matchElementCount">The exact number of elements to that the <paramref name="cssSelector"/> should match.</param>
/// <param name="timeout">The maximum time to wait for elements to appear.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
public static IRefreshableElementCollection<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount, TimeSpan timeout)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
public static IReadOnlyList<IElement> WaitForElements<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount, TimeSpan timeout)
where TComponent : IComponent
=> WaitForElementsCore(renderedComponent, cssSelector, matchElementCount: matchElementCount, timeout: timeout);

Expand Down Expand Up @@ -115,8 +115,8 @@ internal static Task<IElement> WaitForElementAsync<TComponent>(this IRenderedCom
/// <param name="cssSelector">The CSS selector to use to search for elements.</param>
/// <param name="matchElementCount">The exact number of elements to that the <paramref name="cssSelector"/> should match.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
internal static Task<IRefreshableElementCollection<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
internal static Task<IReadOnlyList<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount)
where TComponent : IComponent
=> WaitForElementsCoreAsync(renderedComponent, cssSelector, matchElementCount: matchElementCount, timeout: null);

Expand All @@ -128,8 +128,8 @@ internal static Task<IRefreshableElementCollection<IElement>> WaitForElementsAsy
/// <param name="cssSelector">The CSS selector to use to search for elements.</param>
/// <param name="timeout">The maximum time to wait for elements to appear.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
internal static Task<IRefreshableElementCollection<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, TimeSpan timeout)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
internal static Task<IReadOnlyList<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, TimeSpan timeout)
where TComponent : IComponent
=> WaitForElementsCoreAsync(renderedComponent, cssSelector, matchElementCount: null, timeout: timeout);

Expand All @@ -142,8 +142,8 @@ internal static Task<IRefreshableElementCollection<IElement>> WaitForElementsAsy
/// <param name="matchElementCount">The exact number of elements to that the <paramref name="cssSelector"/> should match.</param>
/// <param name="timeout">The maximum time to wait for elements to appear.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
internal static Task<IRefreshableElementCollection<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount, TimeSpan timeout)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
internal static Task<IReadOnlyList<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector, int matchElementCount, TimeSpan timeout)
where TComponent : IComponent
=> WaitForElementsCoreAsync(renderedComponent, cssSelector, matchElementCount: matchElementCount, timeout: timeout);

Expand All @@ -154,8 +154,8 @@ internal static Task<IRefreshableElementCollection<IElement>> WaitForElementsAsy
/// <param name="renderedComponent">The render fragment or component find the matching element in.</param>
/// <param name="cssSelector">The CSS selector to use to search for elements.</param>
/// <exception cref="WaitForFailedException">Thrown if no elements is found matching the <paramref name="cssSelector"/> within the default timeout.</exception>
/// <returns>The <see cref="IRefreshableElementCollection{IElement}"/>.</returns>
internal static Task<IRefreshableElementCollection<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
/// <returns>The <see cref="IReadOnlyList{IElement}"/>.</returns>
internal static Task<IReadOnlyList<IElement>> WaitForElementsAsync<TComponent>(this IRenderedComponent<TComponent> renderedComponent, string cssSelector)
where TComponent : IComponent
=> WaitForElementsCoreAsync<TComponent>(renderedComponent, cssSelector, matchElementCount: null, timeout: null);

Expand Down Expand Up @@ -185,7 +185,7 @@ private static async Task<IElement> WaitForElementCoreAsync<TComponent>(this IRe
return await waiter.WaitTask;
}

private static IRefreshableElementCollection<IElement> WaitForElementsCore<TComponent>(
private static IReadOnlyList<IElement> WaitForElementsCore<TComponent>(
this IRenderedComponent<TComponent> renderedComponent,
string cssSelector,
int? matchElementCount,
Expand All @@ -207,7 +207,7 @@ private static IRefreshableElementCollection<IElement> WaitForElementsCore<TComp
}
}

private static async Task<IRefreshableElementCollection<IElement>> WaitForElementsCoreAsync<TComponent>(
private static async Task<IReadOnlyList<IElement>> WaitForElementsCoreAsync<TComponent>(
this IRenderedComponent<TComponent> renderedComponent,
string cssSelector,
int? matchElementCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Bunit.Extensions.WaitForHelpers;
/// <summary>
/// Represents an async wait helper, that will wait for a specified time for element(s) to become available in the DOM.
/// </summary>
internal class WaitForElementsHelper<TComponent> : WaitForHelper<IRefreshableElementCollection<IElement>, TComponent>
internal class WaitForElementsHelper<TComponent> : WaitForHelper<IReadOnlyList<IElement>, TComponent>
where TComponent : IComponent
{
internal const string TimeoutBeforeFoundMessage = "The CSS selector did not result in any matching element(s) before the timeout period passed.";
Expand Down
Loading