From fa4904eb4aadf96e4b3a4fb4ce7944a841bf0aae Mon Sep 17 00:00:00 2001 From: bUnit bot Date: Sat, 2 Mar 2024 16:08:50 +0000 Subject: [PATCH 01/31] Set version to '1.28-preview' --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index da1b5579f..a3427a8f8 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.27-preview", + "version": "1.28-preview", "assemblyVersion": { "precision": "revision" }, From e14ac76a01ba50412de623f231294efc2cc8ccb3 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sun, 3 Mar 2024 00:04:14 +0100 Subject: [PATCH 02/31] fix: Comment in TestContextBase --- src/bunit.core/TestContextBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bunit.core/TestContextBase.cs b/src/bunit.core/TestContextBase.cs index 345bc8cbb..04f07f265 100644 --- a/src/bunit.core/TestContextBase.cs +++ b/src/bunit.core/TestContextBase.cs @@ -22,9 +22,8 @@ public abstract class TestContextBase : IDisposable public ITestRenderer Renderer => testRenderer ??= CreateTestRenderer(); /// - /// Hey YouTube, I'm a comment! + /// Creates the renderer. /// - /// protected abstract ITestRenderer CreateTestRenderer(); /// From 3f6d5b61aa8a41e779942ca846fbc96219235371 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 09:30:52 +0000 Subject: [PATCH 03/31] build(deps): Bump SonarAnalyzer.CSharp from 9.20.0.85982 to 9.21.0.86780 (#1407) Bumps [SonarAnalyzer.CSharp](https://github.com/SonarSource/sonar-dotnet) from 9.20.0.85982 to 9.21.0.86780. - [Release notes](https://github.com/SonarSource/sonar-dotnet/releases) - [Commits](https://github.com/SonarSource/sonar-dotnet/compare/9.20.0.85982...9.21.0.86780) --- updated-dependencies: - dependency-name: SonarAnalyzer.CSharp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 09f9fb2ef..20077a30f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,7 +52,7 @@ - + Date: Wed, 6 Mar 2024 21:27:01 +0100 Subject: [PATCH 04/31] Fix 1408 (#1411) * fix: Make NavigationManager singleton fixes #1408 * chore: Bump Packages --- CHANGELOG.md | 3 +++ .../bunit.generators.internal.csproj | 2 +- src/bunit.web/Extensions/TestServiceProviderExtensions.cs | 2 +- src/bunit.web/bunit.web.csproj | 8 ++++---- .../bunit.generators.tests/bunit.generators.tests.csproj | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e3ce2e19..9c6e9418f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ All notable changes to **bUnit** will be documented in this file. The project ad ## [Unreleased] +### Changed +- `NavigationManager` is again registered as a singleton instead of scoped. + ## [1.27.17] - 2024-03-02 ### Added diff --git a/src/bunit.generators.internal/bunit.generators.internal.csproj b/src/bunit.generators.internal/bunit.generators.internal.csproj index ef7414330..d82319f81 100644 --- a/src/bunit.generators.internal/bunit.generators.internal.csproj +++ b/src/bunit.generators.internal/bunit.generators.internal.csproj @@ -15,7 +15,7 @@ - + all diff --git a/src/bunit.web/Extensions/TestServiceProviderExtensions.cs b/src/bunit.web/Extensions/TestServiceProviderExtensions.cs index 1b23ec835..e23f5b002 100644 --- a/src/bunit.web/Extensions/TestServiceProviderExtensions.cs +++ b/src/bunit.web/Extensions/TestServiceProviderExtensions.cs @@ -38,7 +38,7 @@ public static IServiceCollection AddDefaultTestContextServices(this IServiceColl // bUnits fake Navigation Manager services.AddSingleton(); - services.AddScoped(s => s.GetRequiredService()); + services.AddSingleton(s => s.GetRequiredService()); services.AddSingleton(); // bUnits fake WebAssemblyHostEnvironment diff --git a/src/bunit.web/bunit.web.csproj b/src/bunit.web/bunit.web.csproj index 67a137951..530924aa6 100644 --- a/src/bunit.web/bunit.web.csproj +++ b/src/bunit.web/bunit.web.csproj @@ -44,7 +44,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -66,7 +66,7 @@ - + @@ -77,7 +77,7 @@ - + diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index dd148fcf9..8ad2c2b26 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -17,7 +17,7 @@ - + From 48cc55b107e84fef2228a96fc1f6be77d27e20d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:14:15 +0000 Subject: [PATCH 05/31] build(deps): Bump Verify.Xunit from 23.2.3 to 23.3.0 Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 23.2.3 to 23.3.0. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/23.2.3...23.3.0) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- tests/bunit.generators.tests/bunit.generators.tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index 8ad2c2b26..4356ab704 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -17,7 +17,7 @@ - + From e2beaea6f03c9a36acf946de5d320121f6101f12 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Thu, 7 Mar 2024 19:19:00 +0100 Subject: [PATCH 06/31] chore: Update Source Code generator libs --- .../bunit.generators.internal.csproj | 2 +- src/bunit.generators/bunit.generators.csproj | 2 +- .../bunit.generators.tests/bunit.generators.tests.csproj | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bunit.generators.internal/bunit.generators.internal.csproj b/src/bunit.generators.internal/bunit.generators.internal.csproj index d82319f81..bedae7e2e 100644 --- a/src/bunit.generators.internal/bunit.generators.internal.csproj +++ b/src/bunit.generators.internal/bunit.generators.internal.csproj @@ -16,7 +16,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/bunit.generators/bunit.generators.csproj b/src/bunit.generators/bunit.generators.csproj index a43bf6de0..694166773 100644 --- a/src/bunit.generators/bunit.generators.csproj +++ b/src/bunit.generators/bunit.generators.csproj @@ -70,7 +70,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index 4356ab704..3b2c3e61c 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -19,10 +19,10 @@ - - - - + + + + From 6a200dea3cb31f52631f0d6f178b64fc5e988f92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 18:17:27 +0100 Subject: [PATCH 07/31] build(deps): Bump Verify.Xunit from 23.3.0 to 23.4.0 (#1417) Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 23.3.0 to 23.4.0. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/23.3.0...23.4.0) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/bunit.generators.tests/bunit.generators.tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index 3b2c3e61c..cf17eb549 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -17,7 +17,7 @@ - + From 3ad9984a06abe1722d5f8720b9c0b7563dccedc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:53:28 +0100 Subject: [PATCH 08/31] build(deps): Bump Verify.Xunit from 23.4.0 to 23.5.0 (#1418) Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 23.4.0 to 23.5.0. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/23.4.0...23.5.0) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/bunit.generators.tests/bunit.generators.tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index cf17eb549..b11231c33 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -17,7 +17,7 @@ - + From c40735b7c1d0287e0c7e0b33e5dcb2175443d087 Mon Sep 17 00:00:00 2001 From: Scott Sauber Date: Sun, 17 Mar 2024 13:23:44 -0700 Subject: [PATCH 09/31] feat: Add FindByLabelText to find elements by the text of their labels (#1252) * add label, aria-label, wrapped label * switch to strategy pattern * feat: support for all element types that can have a label * feat: support for all element types that can have a wrapped label * feat: support for all element types that can have an aria-label * feat: support for all element types that can have an aria-labelledby * style: remove comment * fix: use theorydata * fix: use method instead of list * failing test to prove the re-rendered element issue * move to element factory to prevent re-renders causing issues * fix: switch to array for strategies * fix: remove todos * fix: move to new ielementwrapperfactory * fix: use custom labelnotfoundexception * feat: support for different casing sensitivity * chore: add xml docs to indicate defaults of ByLabelTextOptions * fix: make classes add public * fix: remove project references * fix: move to source generator to public * chore: switch to use wrapper component for tests * chore: switch to use wrapper component for tests * chore: rename to labelquerycounter for re-rendering test * fix: remove warnings * refactor: remove string duplication in tests * fix: remove nullability warning * fix: add sealed to remove warnings * feat: add support for whitespace * add xml comments * fix: labelElement can be null if not found * chore: fix indention * chore: remove duplicated word * fix: make label options immutable * test: verify generate test output * chore: remove whitespace in test case name * refactor: simplify null check * fix: cover scenario where wrapped label has nested HTML * fix: rename test * test: add additional test for nested html with for attributes * docs: cover FindByLabelText and update verify markup section to discuss different markup verify approaches * docs: verify-markup.md Co-authored-by: Steven Giesel * refactor: use configure options pattern instead of passing option object --------- Co-authored-by: Egil Hansen Co-authored-by: Steven Giesel --- docs/site/docs/verification/verify-markup.md | 111 +++--- .../Web.AngleSharp/IElementWrapper.cs | 2 +- .../Web.AngleSharp/IElementWrapperFactory.cs | 2 +- .../Web.AngleSharp/WrapperBase.cs | 2 +- .../WrapperElementsGenerator.cs | 8 +- .../ByLabelTextElementFactory.cs | 31 ++ .../Labels/ByLabelTextOptions.cs | 17 + .../Labels/LabelElementExtensions.cs | 18 + .../Labels/LabelNotFoundException.cs | 31 ++ .../Labels/LabelQueryExtensions.cs | 50 +++ .../Strategies/ILabelTextQueryStrategy.cs | 8 + .../LabelTextUsingAriaLabelStrategy.cs | 25 ++ .../LabelTextUsingAriaLabelledByStrategy.cs | 21 ++ .../LabelTextUsingForAttributeStrategy.cs | 23 ++ .../LabelTextUsingWrappedElementStrategy.cs | 19 + src/bunit.web.query/NodeListExtensions.cs | 32 ++ src/bunit.web.query/bunit.web.query.csproj | 8 + ...st.Generator#IElementWrapper.g.verified.cs | 2 +- ...rator#IElementWrapperFactory.g.verified.cs | 2 +- ...orTest.Generator#WrapperBase.g.verified.cs | 2 +- ....Generator#WrapperExtensions.g.verified.cs | 8 +- .../BlazorE2E/LabelQueryCounter.razor | 19 + .../Labels/LabelQueryExtensionsTests.cs | 354 ++++++++++++++++++ 23 files changed, 734 insertions(+), 61 deletions(-) create mode 100644 src/bunit.web.query/ByLabelTextElementFactory.cs create mode 100644 src/bunit.web.query/Labels/ByLabelTextOptions.cs create mode 100644 src/bunit.web.query/Labels/LabelElementExtensions.cs create mode 100644 src/bunit.web.query/Labels/LabelNotFoundException.cs create mode 100644 src/bunit.web.query/Labels/LabelQueryExtensions.cs create mode 100644 src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs create mode 100644 src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs create mode 100644 src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs create mode 100644 src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs create mode 100644 src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs create mode 100644 src/bunit.web.query/NodeListExtensions.cs create mode 100644 tests/bunit.testassets/BlazorE2E/LabelQueryCounter.razor create mode 100644 tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTests.cs diff --git a/docs/site/docs/verification/verify-markup.md b/docs/site/docs/verification/verify-markup.md index f9c6c2c6b..54d02678d 100644 --- a/docs/site/docs/verification/verify-markup.md +++ b/docs/site/docs/verification/verify-markup.md @@ -5,34 +5,63 @@ title: Verifying markup from a component # Verifying markup from a component -When a component is rendered in a test, the result is a or a . Through these, it is possible to access the rendered markup (HTML) of the component and, in the case of , the instance of the component. +Generally, the strategy for verifying markup produced by components depends on whether you are creating reusable component library or a single-use Blazor app component. -> [!NOTE] -> An inherits from . This page will only cover features of the type. is covered on the page. +With a **reusable component library**, the markup produced may be considered part of the externally observable behavior of the component, and that should thus be verified, since users of the component may depend on the markup having a specific structure. Consider using `MarkupMatches` and semantic comparison described below to get the best protection against regressions and good maintainability. + +When **building components for a Blazor app**, the externally observable behavior of components are how they visibly look and behave from an end-users point of view, e.g. what the user sees and interact with in a browser. In this scenario, consider use `FindByLabelText` and related methods described below to inspect and assert against individual elements look and feel, for a good balance between protection against regressions and maintainability. Learn more about this testing approach at https://testing-library.com. This page covers the following **verification approaches:** -- Basic verification of raw markup -- Semantic comparison of markup - Inspecting the individual DOM nodes in the DOM tree +- Semantic comparison of markup - Finding expected differences in markup between renders +- Verification of raw markup The following sections will cover each of these. -## Basic verification of raw markup +## Result of rendering components -To access the rendered markup of a component, just use the property on . This holds the *raw* HTML from the component as a `string`. +When a component is rendered in a test, the result is a or a . Through these, it is possible to access the rendered markup (HTML) of the component and, in the case of , the instance of the component. -> [!WARNING] -> Be aware that all indentions and whitespace in your components (`.razor` files) are included in the raw rendered markup, so it is often wise to normalize the markup string a little. For example, via the string `Trim()` method to make the tests more stable. Otherwise, a change to the formatting in your components might break the tests unnecessarily when it does not need to. -> -> To avoid these issues and others related to asserting against raw markup, use the semantic HTML comparer that comes with bUnit, described in the next section. +> [!NOTE] +> An inherits from . This page will only cover features of the type. is covered on the page. -To get the markup as a string, do the following: +## Inspecting DOM nodes -[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=16&end=19&highlight=3)] +The rendered markup from a component is available as a DOM node through the property on . The nodes and element types comes from [AngleSharp](https://anglesharp.github.io/) that follows the W3C DOM API specifications and gives you the same results as a state-of-the-art browser’s implementation of the DOM API in JavaScript. Besides the official DOM API, AngleSharp and bUnit add some useful extension methods on top. This makes working with DOM nodes convenient. + +### Finding DOM elements + +bUnit supports multiple different ways of searching and querying the rendered HTML elements: + +- `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.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,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.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) 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.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) and [`FindAll(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) methods to query the `` component listed below. + +[!code-razor[FancyTable.razor](../../../samples/components/FancyTable.razor)] + +To find the `` element and the first `` elements in each row, do the following: + +[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=54&end=57&highlight=3-4)] + +Once you have one or more elements, you verify against them, such as by inspecting their properties through the DOM API. For example: -You can perform standard string assertions against the markup string, like checking whether it contains a value or is empty. +[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=59&end=61)] + +#### Auto-refreshing Find() queries + +An element found with the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) method will be updated if the component it came from is re-rendered. + +However, that does not apply to elements that are found by traversing the DOM tree via the property on , for example, as those nodes do not know when their root component is re-rendered. Consequently, they don’t know when they should be updated. + +As a result of this, it is always recommended to use the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,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.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,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 @@ -91,45 +120,6 @@ The semantic HTML comparer can be customized to make a test case even more stabl Learn more about the customization options on the page. -## Inspecting DOM nodes - -The rendered markup from a component is available as a DOM node through the property on , as well as the `Find(string cssSelector)` and `FindAll(string cssSelector)` extension methods on . - -The property and the `FindAll()` method return an [AngleSharp](https://anglesharp.github.io/) `INodeList` type, and the `Find()` method returns an [AngleSharp](https://anglesharp.github.io/) `IElement` type. - -The DOM API in AngleSharp follows the W3C DOM API specifications and gives you the same results as a state-of-the-art browser’s implementation of the DOM API in JavaScript. Besides the official DOM API, AngleSharp and bUnit add some useful extension methods on top. This makes working with DOM nodes convenient. - -### Finding nodes with the Find() and FindAll() methods - -Users of the famous JavaScript framework [jQuery](https://jquery.com/) will recognize these two methods: - -- [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,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.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) 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.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) and [`FindAll(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,System.String,System.Boolean)) methods to query the `` component listed below. - -[!code-razor[FancyTable.razor](../../../samples/components/FancyTable.razor)] - -To find the `` element and the first `` elements in each row, do the following: - -[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=54&end=57&highlight=3-4)] - -Once you have one or more elements, you verify against them, such as by inspecting their properties through the DOM API. For example: - -[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=59&end=61)] - -#### Auto-refreshing Find() queries - -An element found with the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) method will be updated if the component it came from is re-rendered. - -However, that does not apply to elements that are found by traversing the DOM tree via the property on , for example, as those nodes do not know when their root component is re-rendered. Consequently, they don’t know when they should be updated. - -As a result of this, it is always recommended to use the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,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.RenderedFragmentExtensions.FindAll(Bunit.IRenderedFragment,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. - ## Finding expected differences It can sometimes be easier to verify that an expected change, and only that change, has occurred in the rendered markup than it can be to specify how all the rendered markup should look after re-rendering. @@ -178,3 +168,18 @@ This is what happens in the test: 8. Finally the last item in the list is found and clicked, and the method is used to find the changes, a single diff, which is verified as a removal of the second item. As mentioned earlier, the `IDiff` assertion helpers are still experimental. Any feedback and suggestions for improvements should be directed to the [related issue](https://github.com/egil/bUnit/issues/84) on GitHub. + +## Verification of raw markup + +To access the rendered markup of a component, just use the property on . This holds the *raw* HTML from the component as a `string`. + +> [!WARNING] +> Be aware that all indentions and whitespace in your components (`.razor` files) are included in the raw rendered markup, so it is often wise to normalize the markup string a little. For example, via the string `Trim()` method to make the tests more stable. Otherwise, a change to the formatting in your components might break the tests unnecessarily when it does not need to. +> +> To avoid these issues and others related to asserting against raw markup, use the semantic HTML comparer that comes with bUnit, described in the next section. + +To get the markup as a string, do the following: + +[!code-csharp[](../../../samples/tests/xunit/VerifyMarkupExamples.cs?start=16&end=19&highlight=3)] + +Standard string assertions can be performed against the markup string, such as checking whether it contains a value or is empty. \ No newline at end of file diff --git a/src/bunit.generators.internal/Web.AngleSharp/IElementWrapper.cs b/src/bunit.generators.internal/Web.AngleSharp/IElementWrapper.cs index 13bbc1e2b..de2596bdc 100644 --- a/src/bunit.generators.internal/Web.AngleSharp/IElementWrapper.cs +++ b/src/bunit.generators.internal/Web.AngleSharp/IElementWrapper.cs @@ -8,7 +8,7 @@ namespace Bunit.Web.AngleSharp; /// Represents a wrapper around an . /// [GeneratedCodeAttribute("Bunit.Web.AngleSharp", "1.0.0.0")] -internal interface IElementWrapper where TElement : class, IElement +public interface IElementWrapper where TElement : class, IElement { /// /// Gets the wrapped element. diff --git a/src/bunit.generators.internal/Web.AngleSharp/IElementWrapperFactory.cs b/src/bunit.generators.internal/Web.AngleSharp/IElementWrapperFactory.cs index 0b5e5ce98..86f13f3c5 100644 --- a/src/bunit.generators.internal/Web.AngleSharp/IElementWrapperFactory.cs +++ b/src/bunit.generators.internal/Web.AngleSharp/IElementWrapperFactory.cs @@ -9,7 +9,7 @@ namespace Bunit.Web.AngleSharp; /// Represents an factory, used by a . /// [GeneratedCodeAttribute("Bunit.Web.AngleSharp", "1.0.0.0")] -internal interface IElementWrapperFactory +public interface IElementWrapperFactory { /// /// A method that returns the latest version of the element to wrap. diff --git a/src/bunit.generators.internal/Web.AngleSharp/WrapperBase.cs b/src/bunit.generators.internal/Web.AngleSharp/WrapperBase.cs index f24e9e124..8dd4d5963 100644 --- a/src/bunit.generators.internal/Web.AngleSharp/WrapperBase.cs +++ b/src/bunit.generators.internal/Web.AngleSharp/WrapperBase.cs @@ -11,7 +11,7 @@ namespace Bunit.Web.AngleSharp; /// [DebuggerNonUserCode] [GeneratedCodeAttribute("Bunit.Web.AngleSharp", "1.0.0.0")] -internal abstract class WrapperBase : IElementWrapper +public abstract class WrapperBase : IElementWrapper where TElement : class, IElement { private readonly IElementWrapperFactory elementFactory; diff --git a/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs b/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs index 8d7f6d921..facb2e810 100644 --- a/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs +++ b/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs @@ -65,10 +65,16 @@ private static void GenerateWrapperFactory(StringBuilder source, IEnumerable"); + source.AppendLine("/// Provide helpers for wrapped HTML elements."); + source.AppendLine("/// "); source.AppendLine("[System.CodeDom.Compiler.GeneratedCodeAttribute(\"Bunit.Web.AngleSharp\", \"1.0.0.0\")]"); - source.AppendLine($"internal static class WrapperExtensions"); + source.AppendLine("public static class WrapperExtensions"); source.AppendLine("{"); source.AppendLine(); + source.AppendLine("/// "); + source.AppendLine("/// Provide wrapper to be used when elements re-render."); + source.AppendLine("/// "); source.AppendLine($"\tpublic static global::AngleSharp.Dom.IElement WrapUsing(this global::AngleSharp.Dom.IElement element, TElementFactory elementFactory) where TElementFactory : Bunit.Web.AngleSharp.IElementWrapperFactory => element switch"); source.AppendLine("\t{"); diff --git a/src/bunit.web.query/ByLabelTextElementFactory.cs b/src/bunit.web.query/ByLabelTextElementFactory.cs new file mode 100644 index 000000000..eef786319 --- /dev/null +++ b/src/bunit.web.query/ByLabelTextElementFactory.cs @@ -0,0 +1,31 @@ +using AngleSharp.Dom; +using Bunit.Web.AngleSharp; + +namespace Bunit; + +internal sealed class ByLabelTextElementFactory : IElementWrapperFactory +{ + private readonly IRenderedFragment testTarget; + private readonly string labelText; + private readonly ByLabelTextOptions options; + + public Action? OnElementReplaced { get; set; } + + public ByLabelTextElementFactory(IRenderedFragment testTarget, string labelText, ByLabelTextOptions options) + { + this.testTarget = testTarget; + this.labelText = labelText; + this.options = options; + testTarget.OnMarkupUpdated += FragmentsMarkupUpdated; + } + + private void FragmentsMarkupUpdated(object? sender, EventArgs args) + => OnElementReplaced?.Invoke(); + + public TElement GetElement() where TElement : class, IElement + { + var element = testTarget.FindByLabelTextInternal(labelText, options) as TElement; + + return element ?? throw new ElementRemovedFromDomException(labelText); + } +} diff --git a/src/bunit.web.query/Labels/ByLabelTextOptions.cs b/src/bunit.web.query/Labels/ByLabelTextOptions.cs new file mode 100644 index 000000000..17872fc35 --- /dev/null +++ b/src/bunit.web.query/Labels/ByLabelTextOptions.cs @@ -0,0 +1,17 @@ +namespace Bunit; + +/// +/// Allows overrides of behavior for FindByLabelText method +/// +public record class ByLabelTextOptions +{ + /// + /// The default behavior used by FindByLabelText if no overrides are specified + /// + internal static readonly ByLabelTextOptions Default = new(); + + /// + /// The StringComparison used for comparing the desired Label Text to the resulting HTML. Defaults to Ordinal (case sensitive). + /// + public StringComparison ComparisonType { get; set; } = StringComparison.Ordinal; +} diff --git a/src/bunit.web.query/Labels/LabelElementExtensions.cs b/src/bunit.web.query/Labels/LabelElementExtensions.cs new file mode 100644 index 000000000..3575210f1 --- /dev/null +++ b/src/bunit.web.query/Labels/LabelElementExtensions.cs @@ -0,0 +1,18 @@ +using AngleSharp.Dom; + +namespace Bunit; + +internal static class LabelElementExtensions +{ + internal static bool IsHtmlElementThatCanHaveALabel(this IElement element) => element.NodeName switch + { + "INPUT" => true, + "SELECT" => true, + "TEXTAREA" => true, + "BUTTON" => true, + "METER" => true, + "OUTPUT" => true, + "PROGRESS" => true, + _ => false + }; +} diff --git a/src/bunit.web.query/Labels/LabelNotFoundException.cs b/src/bunit.web.query/Labels/LabelNotFoundException.cs new file mode 100644 index 000000000..324eaf8ff --- /dev/null +++ b/src/bunit.web.query/Labels/LabelNotFoundException.cs @@ -0,0 +1,31 @@ +namespace Bunit; + +/// +/// Represents a failure to find an element in the searched target +/// using the Label's text. +/// +[Serializable] +public class LabelNotFoundException : Exception +{ + /// + /// Gets the Label Text used to search with. + /// + public string LabelText { get; } = ""; + + /// + /// Initializes a new instance of the class. + /// + /// + public LabelNotFoundException(string labelText) + : base($"Unable to find a label with the text of '{labelText}'.") + { + LabelText = labelText; + } + + + /// + /// Initializes a new instance of the class. + /// + protected LabelNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) { } +} diff --git a/src/bunit.web.query/Labels/LabelQueryExtensions.cs b/src/bunit.web.query/Labels/LabelQueryExtensions.cs new file mode 100644 index 000000000..8d7c42fad --- /dev/null +++ b/src/bunit.web.query/Labels/LabelQueryExtensions.cs @@ -0,0 +1,50 @@ +using AngleSharp.Dom; +using Bunit.Labels.Strategies; + +namespace Bunit; + +/// +/// Extension methods for querying IRenderedFragments by Label +/// +public static class LabelQueryExtensions +{ + private static readonly IReadOnlyList LabelTextQueryStrategies = + [ + // This is intentionally in the order of most likely to minimize strategies tried to find the label + new LabelTextUsingForAttributeStrategy(), + new LabelTextUsingAriaLabelStrategy(), + new LabelTextUsingWrappedElementStrategy(), + new LabelTextUsingAriaLabelledByStrategy(), + ]; + + /// + /// Returns the first element (i.e. an input, select, textarea, etc. element) associated with the given label text. + /// + /// The rendered fragment to search. + /// The text of the label to search (i.e. the InnerText of the Label, such as "First Name" for a ``) + /// Method used to override the default behavior of FindByLabelText. + public static IElement FindByLabelText(this IRenderedFragment renderedFragment, string labelText, Action? configureOptions = null) + { + var options = ByLabelTextOptions.Default; + if (configureOptions is not null) + { + options = options with { }; + configureOptions.Invoke(options); + } + + return FindByLabelTextInternal(renderedFragment, labelText, options) ?? throw new LabelNotFoundException(labelText); + } + + internal static IElement? FindByLabelTextInternal(this IRenderedFragment renderedFragment, string labelText, ByLabelTextOptions options) + { + foreach (var strategy in LabelTextQueryStrategies) + { + var element = strategy.FindElement(renderedFragment, labelText, options); + + if (element is not null) + return element; + } + + return null; + } +} diff --git a/src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs b/src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs new file mode 100644 index 000000000..3c1ba8ea5 --- /dev/null +++ b/src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs @@ -0,0 +1,8 @@ +using AngleSharp.Dom; + +namespace Bunit.Labels.Strategies; + +internal interface ILabelTextQueryStrategy +{ + IElement? FindElement(IRenderedFragment renderedFragment, string labelText, ByLabelTextOptions options); +} diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs new file mode 100644 index 000000000..2ef029809 --- /dev/null +++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs @@ -0,0 +1,25 @@ +using AngleSharp.Dom; +using Bunit.Web.AngleSharp; + +namespace Bunit.Labels.Strategies; + +internal sealed class LabelTextUsingAriaLabelStrategy : ILabelTextQueryStrategy +{ + public IElement? FindElement(IRenderedFragment renderedFragment, string labelText, ByLabelTextOptions options) + { + var caseSensitivityQualifier = options.ComparisonType switch + { + StringComparison.OrdinalIgnoreCase => "i", + StringComparison.InvariantCultureIgnoreCase => "i", + StringComparison.CurrentCultureIgnoreCase => "i", + _ => "" + }; + + var element = renderedFragment.Nodes.TryQuerySelector($"[aria-label='{labelText}'{caseSensitivityQualifier}]"); + + if (element is null) + return null; + + return element.WrapUsing(new ByLabelTextElementFactory(renderedFragment, labelText, options)); + } +} diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs new file mode 100644 index 000000000..3c81e47ac --- /dev/null +++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs @@ -0,0 +1,21 @@ +using AngleSharp.Dom; +using Bunit.Web.AngleSharp; + +namespace Bunit.Labels.Strategies; + +internal sealed class LabelTextUsingAriaLabelledByStrategy : ILabelTextQueryStrategy +{ + public IElement? FindElement(IRenderedFragment renderedFragment, string labelText, ByLabelTextOptions options) + { + var elementsWithAriaLabelledBy = renderedFragment.Nodes.TryQuerySelectorAll("[aria-labelledby]"); + + foreach (var element in elementsWithAriaLabelledBy) + { + var labelElement = renderedFragment.Nodes.TryQuerySelector($"#{element.GetAttribute("aria-labelledby")}"); + if (labelElement is not null && labelElement.GetInnerText().Equals(labelText, options.ComparisonType)) + return element.WrapUsing(new ByLabelTextElementFactory(renderedFragment, labelText, options)); + } + + return null; + } +} diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs new file mode 100644 index 000000000..af3eb91f8 --- /dev/null +++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs @@ -0,0 +1,23 @@ +using AngleSharp.Dom; +using Bunit.Web.AngleSharp; + +namespace Bunit.Labels.Strategies; + +internal sealed class LabelTextUsingForAttributeStrategy : ILabelTextQueryStrategy +{ + public IElement? FindElement(IRenderedFragment renderedFragment, string labelText, ByLabelTextOptions options) + { + var matchingLabel = renderedFragment.Nodes.TryQuerySelectorAll("label") + .SingleOrDefault(l => l.TextContent.Trim().Equals(labelText, options.ComparisonType)); + + if (matchingLabel is null) + return null; + + var matchingElement = renderedFragment.Nodes.TryQuerySelector($"#{matchingLabel.GetAttribute("for")}"); + + if (matchingElement is null) + return null; + + return matchingElement.WrapUsing(new ByLabelTextElementFactory(renderedFragment, labelText, options)); + } +} diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs new file mode 100644 index 000000000..43f47503c --- /dev/null +++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs @@ -0,0 +1,19 @@ +using AngleSharp.Dom; +using Bunit.Web.AngleSharp; + +namespace Bunit.Labels.Strategies; + +internal sealed class LabelTextUsingWrappedElementStrategy : ILabelTextQueryStrategy +{ + public IElement? FindElement(IRenderedFragment renderedFragment, string labelText, ByLabelTextOptions options) + { + var matchingLabel = renderedFragment.Nodes.TryQuerySelectorAll("label") + .SingleOrDefault(l => l.GetInnerText().Trim().StartsWith(labelText, options.ComparisonType)); + + var matchingElement = matchingLabel? + .Children + .SingleOrDefault(n => n.IsHtmlElementThatCanHaveALabel()); + + return matchingElement?.WrapUsing(new ByLabelTextElementFactory(renderedFragment, labelText, options)); + } +} diff --git a/src/bunit.web.query/NodeListExtensions.cs b/src/bunit.web.query/NodeListExtensions.cs new file mode 100644 index 000000000..405476a20 --- /dev/null +++ b/src/bunit.web.query/NodeListExtensions.cs @@ -0,0 +1,32 @@ +using AngleSharp.Css.Dom; +using AngleSharp.Css.Parser; +using AngleSharp.Dom; + +namespace Bunit; + +internal static class NodeListExtensions +{ + internal static IElement? TryQuerySelector(this INodeList nodes, string cssSelector) + { + if (nodes.Length == 0 || + nodes[0].Owner?.Context.GetService() is not ICssSelectorParser cssParser) + return null; + + if (cssParser.ParseSelector(cssSelector) is not ISelector selector) + return null; + + return nodes.QuerySelector(selector); + } + + internal static IEnumerable TryQuerySelectorAll(this INodeList nodes, string cssSelector) + { + if (nodes.Length == 0 || + nodes[0].Owner?.Context.GetService() is not ICssSelectorParser cssParser) + return Enumerable.Empty(); + + if (cssParser.ParseSelector(cssSelector) is not ISelector selector) + return Enumerable.Empty(); + + return nodes.QuerySelectorAll(selector); + } +} diff --git a/src/bunit.web.query/bunit.web.query.csproj b/src/bunit.web.query/bunit.web.query.csproj index 46f303535..89a80ee82 100644 --- a/src/bunit.web.query/bunit.web.query.csproj +++ b/src/bunit.web.query/bunit.web.query.csproj @@ -18,6 +18,10 @@ + + all + runtime; build; native; contentfiles; analyzers + @@ -50,4 +54,8 @@ + + + + diff --git a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapper.g.verified.cs b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapper.g.verified.cs index e6e536021..07d1de153 100644 --- a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapper.g.verified.cs +++ b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapper.g.verified.cs @@ -9,7 +9,7 @@ namespace Bunit.Web.AngleSharp; /// Represents a wrapper around an . /// [GeneratedCodeAttribute("Bunit.Web.AngleSharp", "1.0.0.0")] -internal interface IElementWrapper where TElement : class, IElement +public interface IElementWrapper where TElement : class, IElement { /// /// Gets the wrapped element. diff --git a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapperFactory.g.verified.cs b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapperFactory.g.verified.cs index 6cca6c129..b593cdc42 100644 --- a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapperFactory.g.verified.cs +++ b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#IElementWrapperFactory.g.verified.cs @@ -10,7 +10,7 @@ namespace Bunit.Web.AngleSharp; /// Represents an factory, used by a . /// [GeneratedCodeAttribute("Bunit.Web.AngleSharp", "1.0.0.0")] -internal interface IElementWrapperFactory +public interface IElementWrapperFactory { /// /// A method that returns the latest version of the element to wrap. diff --git a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperBase.g.verified.cs b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperBase.g.verified.cs index b0ea56966..2f8cc5e05 100644 --- a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperBase.g.verified.cs +++ b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperBase.g.verified.cs @@ -12,7 +12,7 @@ namespace Bunit.Web.AngleSharp; /// [DebuggerNonUserCode] [GeneratedCodeAttribute("Bunit.Web.AngleSharp", "1.0.0.0")] -internal abstract class WrapperBase : IElementWrapper +public abstract class WrapperBase : IElementWrapper where TElement : class, IElement { private readonly IElementWrapperFactory elementFactory; diff --git a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperExtensions.g.verified.cs b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperExtensions.g.verified.cs index 9a3d24ff8..5ad42111e 100644 --- a/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperExtensions.g.verified.cs +++ b/tests/bunit.generators.tests/Web.AngleSharp/WrapperElementsGeneratorTest.Generator#WrapperExtensions.g.verified.cs @@ -1,10 +1,16 @@ //HintName: WrapperExtensions.g.cs namespace Bunit.Web.AngleSharp; +/// +/// Provide helpers for wrapped HTML elements. +/// [System.CodeDom.Compiler.GeneratedCodeAttribute("Bunit.Web.AngleSharp", "1.0.0.0")] -internal static class WrapperExtensions +public static class WrapperExtensions { +/// +/// Provide wrapper to be used when elements re-render. +/// public static global::AngleSharp.Dom.IElement WrapUsing(this global::AngleSharp.Dom.IElement element, TElementFactory elementFactory) where TElementFactory : Bunit.Web.AngleSharp.IElementWrapperFactory => element switch { global::AngleSharp.Html.Dom.IHtmlAnchorElement e => new HtmlAnchorElementWrapper(e, elementFactory), diff --git a/tests/bunit.testassets/BlazorE2E/LabelQueryCounter.razor b/tests/bunit.testassets/BlazorE2E/LabelQueryCounter.razor new file mode 100644 index 000000000..65adc4754 --- /dev/null +++ b/tests/bunit.testassets/BlazorE2E/LabelQueryCounter.razor @@ -0,0 +1,19 @@ +@* Testing we get back the re-rendered element for an aria-label *@ + + + + + + + +

Re-rendered input with Aria Labelledby

+ + + + +@code { + private int _count; + public void IncrementCount() => _count++; +} diff --git a/tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTests.cs b/tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTests.cs new file mode 100644 index 000000000..9ee2c2e6c --- /dev/null +++ b/tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTests.cs @@ -0,0 +1,354 @@ +using Bunit.TestAssets.BlazorE2E; + +namespace Bunit.Labels; + +public class LabelQueryExtensionsTests : TestContext +{ + public static TheoryData HtmlElementsThatCanHaveALabel { get; } = new() + { + "input", + "select", + "button", + "meter", + "output", + "progress", + }; + + [Theory(DisplayName = "Should return back associated element with label when using the for attribute with the correct casing")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test001(string htmlElementWithLabel) + { + var labelText = $"Label for {htmlElementWithLabel} 1"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" + + <{htmlElementWithLabel} id="{htmlElementWithLabel}-with-label" /> + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.Id.ShouldBe($"{htmlElementWithLabel}-with-label"); + } + + [Fact(DisplayName = "Should throw exception when label text does not exist in the DOM")] + public void Test002() + { + var expectedLabelText = Guid.NewGuid().ToString(); + var cut = RenderComponent(ps => + ps.AddChildContent($""" + {Guid.NewGuid()} + """)); + + Should.Throw(() => cut.FindByLabelText(expectedLabelText)) + .LabelText.ShouldBe(expectedLabelText); + } + + [Theory(DisplayName = "Should return back element associated with label when is wrapped around element with the correct casing")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test003(string htmlElementWithLabel) + { + var labelText = $"{htmlElementWithLabel} Wrapped Label"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" + + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.Id.ShouldBe($"{htmlElementWithLabel}-wrapped-label"); + } + + [Fact(DisplayName = "Should throw exception when label text exists but is not tied to any input")] + public void Test004() + { + var expectedLabelText = "Label With Missing Input"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" + + """)); + + Should.Throw(() => cut.FindByLabelText(expectedLabelText)) + .LabelText.ShouldBe(expectedLabelText); + } + + [Theory(DisplayName = "Should return back element associated with label when element uses aria-label with the correct casing")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test005(string htmlElementWithLabel) + { + var labelText = $"{htmlElementWithLabel} Aria Label"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" + <{htmlElementWithLabel} id="{htmlElementWithLabel}-with-aria-label" aria-label="{labelText}" /> + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.Id.ShouldBe($"{htmlElementWithLabel}-with-aria-label"); + } + + [Theory(DisplayName = "Should return back element associated with another element that uses aria-labelledby with the correct casing")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test006(string htmlElementWithLabel) + { + var labelText = $"{htmlElementWithLabel} Aria Labelled By"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" +

{labelText}

+ <{htmlElementWithLabel} aria-labelledby="{htmlElementWithLabel}-with-aria-labelledby" /> + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.GetAttribute("aria-labelledby").ShouldBe($"{htmlElementWithLabel}-with-aria-labelledby"); + } + + [Theory(DisplayName = "Should reflect latest value when element re-renders")] + [InlineData("Re-rendered input with label")] + [InlineData("Re-rendered input with wrapped label")] + [InlineData("Re-rendered input With Aria Label")] + [InlineData("Re-rendered input with Aria Labelledby")] + public void Test007(string labelText) + { + var cut = RenderComponent(); + + var input = cut.FindByLabelText(labelText); + input.GetAttribute("value").ShouldBe("0"); + + cut.Find("#increment-button").Click(); + input.GetAttribute("value").ShouldBe("1"); + } + + [Theory(DisplayName = "Should throw LabelNotFoundException when ComparisonType is case sensitive and incorrect casing is used with for attribute")] + [InlineData(StringComparison.Ordinal)] + [InlineData(StringComparison.InvariantCulture)] + [InlineData(StringComparison.CurrentCulture)] + public void Test009(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""""")); + + Should.Throw(() => cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison)) + .LabelText.ShouldBe(expectedLabelText); + } + + [Theory(DisplayName = "Should return back element associated with label when ComparisonType is case insensitive and incorrect casing is used with for attribute")] + [InlineData(StringComparison.OrdinalIgnoreCase)] + [InlineData(StringComparison.InvariantCultureIgnoreCase)] + [InlineData(StringComparison.CurrentCultureIgnoreCase)] + public void Test010(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""""")); + + var input = cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe("INPUT"); + input.Id.ShouldBe("input-1"); + } + + [Theory(DisplayName = "Should throw LabelNotFoundException when ComparisonType is case sensitive and incorrect casing is used with wrapped label")] + [InlineData(StringComparison.Ordinal)] + [InlineData(StringComparison.InvariantCulture)] + [InlineData(StringComparison.CurrentCulture)] + public void Test011(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""""")); + + Should.Throw(() => cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison)) + .LabelText.ShouldBe(expectedLabelText); + } + + [Theory(DisplayName = "Should return back element associated with label when ComparisonType is case insensitive and incorrect casing is used with wrapped label")] + [InlineData(StringComparison.OrdinalIgnoreCase)] + [InlineData(StringComparison.InvariantCultureIgnoreCase)] + [InlineData(StringComparison.CurrentCultureIgnoreCase)] + public void Test012(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""""")); + + var input = cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe("INPUT"); + input.Id.ShouldBe("input-1"); + } + + [Theory(DisplayName = "Should throw LabelNotFoundException when ComparisonType is case sensitive and incorrect casing is used with aria-label")] + [InlineData(StringComparison.Ordinal)] + [InlineData(StringComparison.InvariantCulture)] + [InlineData(StringComparison.CurrentCulture)] + public void Test013(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""""")); + + Should.Throw(() => cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison)) + .LabelText.ShouldBe(expectedLabelText); + } + + [Theory(DisplayName = "Should return back element associated with label when ComparisonType is case insensitive and incorrect casing is used with aria-label")] + [InlineData(StringComparison.OrdinalIgnoreCase)] + [InlineData(StringComparison.InvariantCultureIgnoreCase)] + [InlineData(StringComparison.CurrentCultureIgnoreCase)] + public void Test014(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""""")); + + var input = cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe("INPUT"); + input.Id.ShouldBe("input-1"); + } + + [Theory(DisplayName = "Should throw LabelNotFoundException when ComparisonType is case insensitive and incorrect casing is used with aria-labelledby")] + [InlineData(StringComparison.Ordinal)] + [InlineData(StringComparison.InvariantCulture)] + [InlineData(StringComparison.CurrentCulture)] + public void Test015(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""

Label Text

""")); + + Should.Throw(() => cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison)) + .LabelText.ShouldBe(expectedLabelText); + } + + [Theory(DisplayName = "Should return back element associated with label when ComparisonType is case insensitive and incorrect casing is used with aria-labelledby")] + [InlineData(StringComparison.OrdinalIgnoreCase)] + [InlineData(StringComparison.InvariantCultureIgnoreCase)] + [InlineData(StringComparison.CurrentCultureIgnoreCase)] + public void Test016(StringComparison comparison) + { + var expectedLabelText = "LABEL TEXT"; + var cut = RenderComponent(ps => + ps.AddChildContent("""

Label Text

""")); + + var input = cut.FindByLabelText(expectedLabelText, o => o.ComparisonType = comparison); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe("INPUT"); + input.Id.ShouldBe("input-1"); + } + + [Theory(DisplayName = "Should return back associated element with label when extra spacing exists at the beginning and end of the element")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test017(string htmlElementWithLabel) + { + var labelText = $"Label for {htmlElementWithLabel} 1"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" + + <{htmlElementWithLabel} id="{htmlElementWithLabel}-with-label" /> + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.Id.ShouldBe($"{htmlElementWithLabel}-with-label"); + } + + [Theory(DisplayName = "Should return back element associated with label when label when is wrapped around element with extra spacing at the beginning and end of the element")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test018(string htmlElementWithLabel) + { + var labelText = $"{htmlElementWithLabel} Wrapped Label"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" + + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.Id.ShouldBe($"{htmlElementWithLabel}-wrapped-label"); + } + + [Theory(DisplayName = "Should return back element associated with another element that uses aria-labelledby with extra spacing at the beginning and end")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test019(string htmlElementWithLabel) + { + var labelText = $"{htmlElementWithLabel} Aria Labelled By"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" +

+ {labelText} +

+ <{htmlElementWithLabel} aria-labelledby="{htmlElementWithLabel}-with-aria-labelledby" /> + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.GetAttribute("aria-labelledby").ShouldBe($"{htmlElementWithLabel}-with-aria-labelledby"); + } + + [Theory(DisplayName = "Should return back element associated with label when label is wrapped around element with label containing nested html")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test020(string htmlElementWithLabel) + { + var cut = RenderComponent(ps => + ps.AddChildContent($""" + + """)); + + var input = cut.FindByLabelText("Test Label"); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.Id.ShouldBe($"{htmlElementWithLabel}-wrapped-label"); + } + + [Theory(DisplayName = "Should return back associated element with label when using the for attribute with label containing nested html")] + [MemberData(nameof(HtmlElementsThatCanHaveALabel))] + public void Test021(string htmlElementWithLabel) + { + var labelText = $"Label for {htmlElementWithLabel} 1"; + var cut = RenderComponent(ps => + ps.AddChildContent($""" + + <{htmlElementWithLabel} id="{htmlElementWithLabel}-with-label" /> + """)); + + var input = cut.FindByLabelText(labelText); + + input.ShouldNotBeNull(); + input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase); + input.Id.ShouldBe($"{htmlElementWithLabel}-with-label"); + } +} From b8a8561865c179b6ca5d4db13fdd7653ff93db85 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Sun, 17 Mar 2024 22:38:07 +0000 Subject: [PATCH 10/31] ci: release preview of bunit.web.query --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54d6979c9..341efd5e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,6 +78,7 @@ jobs: dotnet pack src/bunit.web/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.template/ -c Release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true dotnet pack src/bunit.generators/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true + dotnet pack src/bunit.web.query/ -c release --output ${{ env.NUGET_DIRECTORY }} -p:ContinuousIntegrationBuild=true -p:publicrelease=true # Publish the NuGet package as an artifact, so they can be used in the following jobs - uses: actions/upload-artifact@v4 From c29f32cfc99df421cc46ca59f5c6676bea556895 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Mon, 18 Mar 2024 00:21:09 +0000 Subject: [PATCH 11/31] docs: README.md with link to bunit.web.query --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 26fa7dc18..a1f0bb490 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ bUnit is available on NuGet in various incarnations. Most should just pick the [ | [bUnit.web](https://www.nuget.org/packages/bunit.web/) | Adds support for testing Blazor components for the web. This includes bUnit.core. | [![Nuget](https://img.shields.io/nuget/dt/bunit.web?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.web/) | | [bUnit.template](https://www.nuget.org/packages/bunit.template/) | Template, which currently creates xUnit-based bUnit test projects only. | [![Nuget](https://img.shields.io/nuget/dt/bunit.template?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.template/) | | [bUnit.generators](https://www.nuget.org/packages/bunit.generators/)|Source code generators to minimize code setup in various situations.|[![Nuget](https://img.shields.io/nuget/dt/bunit.generators?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.generators/)| +| [bUnit.web.query](https://www.nuget.org/packages/bunit.web.query/)|bUnit implementation of testing-library.com's query APIs.|[![Nuget](https://img.shields.io/nuget/dt/bunit.web.query?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.web.query/)| To get started, head to the [getting started documentation](https://bunit.dev/docs/getting-started) to learn more. From df2749954dbe865dd58ffad50a8839459795b4af Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Mon, 18 Mar 2024 00:22:16 +0000 Subject: [PATCH 12/31] docs: Update index.md with ref to bunit.web.query --- docs/site/index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/site/index.md b/docs/site/index.md index f7f97b08c..a5a62bfe9 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -46,6 +46,7 @@ bUnit is available on NuGet in various incarnations. Most users should just pick | [bUnit.web](https://www.nuget.org/packages/bunit.web/) | Adds support for testing Blazor components for the web. This includes bUnit.core. | [![Nuget](https://img.shields.io/nuget/dt/bunit.web?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.web/) | | [bUnit.template](https://www.nuget.org/packages/bunit.template/) | Template for bUnit test projects based on xUnit, NUnit or MSTest | [![Nuget](https://img.shields.io/nuget/dt/bunit.template?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.template/) | | [bUnit.generators](https://www.nuget.org/packages/bunit.generators/)|Source code generators to minimize code setup in various situations.|[![Nuget](https://img.shields.io/nuget/dt/bunit.generators?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.generators/)| +| [bUnit.web.query](https://www.nuget.org/packages/bunit.web.query/)|bUnit implementation of testing-library.com's query APIs.|[![Nuget](https://img.shields.io/nuget/dt/bunit.web.query?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.web.query/)| ## Sponsors @@ -71,14 +72,14 @@ A huge thank you to the [sponsors of my work with bUnit](https://github.com/spon ## Contributors -Shout outs and a big thank you [to all the contributors](https://github.com/egil/bunit/graphs/contributors) to the library, including those who raise issues, those who provide input to issues, and those who send pull requests. +Shoutouts and a big thank you [to all the contributors](https://github.com/egil/bunit/graphs/contributors) to the library, including those who raise issues, those who provide input to issues, and those who send pull requests. **Want to help out? You can help in a number of ways:** - Provide feedback and input through [issues](https://github.com/egil/bunit/issues), via [Twitter](https://twitter.com/egilhansen) or by using the [bUnit Gitter chat room](https://gitter.im/egil/bunit). -- Help build the library. Just pick an issue and submit pull-requests. +- Help build the library. Just pick an issue and submit pull requests. - Help write documentation. -- Create blog posts, presentations or video tutorials. If you do, I'll be happy to showcase them in the [related section](xref:external-resources) on this site. +- Create blog posts, presentations, or video tutorials. If you do, I'll be happy to showcase them in the [related section](xref:external-resources) on this site. ## Code of conduct From f6d446e2244d49a010891177e65b13696b636687 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Thu, 21 Mar 2024 20:42:06 +0100 Subject: [PATCH 13/31] chore: Bump packages --- Directory.Build.props | 2 +- src/bunit.generators.internal/bunit.generators.internal.csproj | 2 +- src/bunit.generators/bunit.generators.csproj | 2 +- src/bunit.web.query/bunit.web.query.csproj | 2 +- tests/bunit.generators.tests/bunit.generators.tests.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 20077a30f..8ffa9d5b9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,7 +52,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive
- + all runtime; build; native; contentfiles; analyzers diff --git a/src/bunit.generators/bunit.generators.csproj b/src/bunit.generators/bunit.generators.csproj index 694166773..281b1d752 100644 --- a/src/bunit.generators/bunit.generators.csproj +++ b/src/bunit.generators/bunit.generators.csproj @@ -75,7 +75,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers diff --git a/src/bunit.web.query/bunit.web.query.csproj b/src/bunit.web.query/bunit.web.query.csproj index 89a80ee82..f09a40e6a 100644 --- a/src/bunit.web.query/bunit.web.query.csproj +++ b/src/bunit.web.query/bunit.web.query.csproj @@ -18,7 +18,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index b11231c33..fbbd5c3c5 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -17,7 +17,7 @@ - + From 74035d9c913b58762d29ccbd5312850aa52f5375 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Wed, 27 Mar 2024 16:42:27 +0000 Subject: [PATCH 14/31] fix: correctly parse html that starts with a element (#1428) --- CHANGELOG.md | 432 +++++++++--------- src/bunit.web/Rendering/BunitHtmlParser.cs | 3 +- .../Rendering/BunitHtmlParserTest.cs | 25 + 3 files changed, 245 insertions(+), 215 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c6e9418f..6e15c7049 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ All notable changes to **bUnit** will be documented in this file. The project ad ## [Unreleased] +### Fixed + +- bUnit's built-in HTML parser did not correctly parse full HTML documents that included a `` as the first element. Fixed by [@egil](https://github.com/egil). + ### Changed - `NavigationManager` is again registered as a singleton instead of scoped. @@ -13,131 +17,131 @@ All notable changes to **bUnit** will be documented in this file. The project ad ### Added -- Support for `IKeyedServiceProvider` in net8.0. Reported by [@ViRuSTriNiTy](https://github.com/ViRuSTriNiTy). By [@linkdotnet](https://github.com/linkdotnet). -- Support for `net9.0`. NOTE, there is no commitment as of now to support net9.0 in bUnit v1. However. Support for net9.0 may move to a future v2 release of bUnit and be deprecated in v1. However, allowing bUnit to build and work with net9.0 previews allows our users to keep testing! +- Support for `IKeyedServiceProvider` in net8.0. Reported by [@ViRuSTriNiTy](https://github.com/ViRuSTriNiTy). By [@linkdotnet](https://github.com/linkdotnet). +- Support for `net9.0`. NOTE, there is no commitment as of now to support net9.0 in bUnit v1. However. Support for net9.0 may move to a future v2 release of bUnit and be deprecated in v1. However, allowing bUnit to build and work with net9.0 previews allows our users to keep testing! ### Fixed -- Support for `SupplyFromQueryParameter` in net8.0. Reported by [@aayjaychan](https://github.com/aayjaychan). Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). +- Support for `SupplyFromQueryParameter` in net8.0. Reported by [@aayjaychan](https://github.com/aayjaychan). Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). ## [1.26.64] - 2023-12-20 ### Changed -- Upgraded AngleSharp to 1.0.7. Info: The usage of `AngleSharpWrappers` is not needed anymore. Any usage of `Unwrap` should not be needed anymore. +- Upgraded AngleSharp to 1.0.7. Info: The usage of `AngleSharpWrappers` is not needed anymore. Any usage of `Unwrap` should not be needed anymore. ### Fixed -- When the `TestContext` was disposed, the Blazor Renderer itself didn't dispose components under test. By [@linkdotnet](https://github.com/linkdotnet). -- When navigating, the `HistoryEntryState` on `NavigationManager` will be populated. By [@linkdotnet](https://github.com/linkdotnet). +- When the `TestContext` was disposed, the Blazor Renderer itself didn't dispose components under test. By [@linkdotnet](https://github.com/linkdotnet). +- When navigating, the `HistoryEntryState` on `NavigationManager` will be populated. By [@linkdotnet](https://github.com/linkdotnet). ### Added -- New overloads for `IRenderedFragmentBase.InvokeAsync` that allow retrieving the work item's return value. By [@jcparkyn](https://github.com/jcparkyn). +- New overloads for `IRenderedFragmentBase.InvokeAsync` that allow retrieving the work item's return value. By [@jcparkyn](https://github.com/jcparkyn). ## [1.25.3] - 2023-11-14 -- Upgrade all .NET 8 preview dependencies to .NET 8 stable. +- Upgrade all .NET 8 preview dependencies to .NET 8 stable. ## [1.24.10] - 2023-10-14 ### Fixed -- When the `TestContext` was disposed, it disposed of all services via the service provider. However, if there were ongoing renders happening, this could cause inconsistent state in the render tree, since the `TestRenderer` could try to access the service provider to instantiate components. +- When the `TestContext` was disposed, it disposed of all services via the service provider. However, if there were ongoing renders happening, this could cause inconsistent state in the render tree, since the `TestRenderer` could try to access the service provider to instantiate components. This release changes the dispose phase such that the renderer gets disposed first, then the service provider. The disposal of any services that implement `IAsyncDisposable` is now also awaited. Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). Reported by [@BenSchoen](https://github.com/BenSchoen) in . ### Added -- Support for custom service provider factories (`IServiceProviderFactory`). This enables the use of Autofac and other frameworks for dependency injection like on real-world ASP.NET Core / Blazor projects. By [@inf9144](https://github.com/inf9144). +- Support for custom service provider factories (`IServiceProviderFactory`). This enables the use of Autofac and other frameworks for dependency injection like on real-world ASP.NET Core / Blazor projects. By [@inf9144](https://github.com/inf9144). -- Ability to raise the `oncancel` and `onclose` event, that was introduced with .NET 8. +- Ability to raise the `oncancel` and `onclose` event, that was introduced with .NET 8. ## [1.23.9] - 2023-09-06 ### Fixed -- If the renderer was not idle when calling `SetParametersAndRender`, the method could return before the parameters were set and the component under test had finished rendering. This was a regression that happened in v1.21.9. Reported by [@Skintkingle](https://github.com/Skintkingle]) in . Fixed by [@egil](https://github.com/egil). +- If the renderer was not idle when calling `SetParametersAndRender`, the method could return before the parameters were set and the component under test had finished rendering. This was a regression that happened in v1.21.9. Reported by [@Skintkingle](https://github.com/Skintkingle]) in . Fixed by [@egil](https://github.com/egil). ### Added -- `net8.0` support -- Increased timeout of `WaitForAssertion` to infinite when a debugger is attached. By [@linkdotnet](https://github.com/linkdotnet). +- `net8.0` support +- Increased timeout of `WaitForAssertion` to infinite when a debugger is attached. By [@linkdotnet](https://github.com/linkdotnet). ### Fixed -- AngleSharp IElement extension methods do not work with `IRenderedFragment.Find`. Reported by [a2er](https://github.com/a2er). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- AngleSharp IElement extension methods do not work with `IRenderedFragment.Find`. Reported by [a2er](https://github.com/a2er). Fixed by [@linkdotnet](https://github.com/linkdotnet). ## [1.22.19] - 2023-07-28 ### Added -- Update bunit templates to support the target framework version of the project. By [@linkdotnet](https://github.com/linkdotnet). +- Update bunit templates to support the target framework version of the project. By [@linkdotnet](https://github.com/linkdotnet). ### Fixed -- Calling `MarkupMatches(RenderFragment)` from a lambda passed to e.g. `WaitForAssertion` could lead to a deadlock in certain circumstances. Fixed by [@linkdotnet](https://github.com/linkdotnet). Reported by [@uecasm](https://github.com/uecasm) in . +- Calling `MarkupMatches(RenderFragment)` from a lambda passed to e.g. `WaitForAssertion` could lead to a deadlock in certain circumstances. Fixed by [@linkdotnet](https://github.com/linkdotnet). Reported by [@uecasm](https://github.com/uecasm) in . -- Rendering complex component hierarchies could result in a stack overflow. Fixed by [@egil](https://github.com/egil).. Reported by [@groogiam](https://github.com/groogiam) in . +- Rendering complex component hierarchies could result in a stack overflow. Fixed by [@egil](https://github.com/egil).. Reported by [@groogiam](https://github.com/groogiam) in . -- Remove capturing and dispatching markup updates to test frameworks synchronization context again. This could cause deadlocks and does not have any impact on test stability. Fixed by [@egil](https://github.com/egil). Reported by [@biohazard999](https://github.com/biohazard999) in . +- Remove capturing and dispatching markup updates to test frameworks synchronization context again. This could cause deadlocks and does not have any impact on test stability. Fixed by [@egil](https://github.com/egil). Reported by [@biohazard999](https://github.com/biohazard999) in . ## [1.21.9] - 2023-07-02 ### Fixed -- Allow using 3rd party `IComponentActivator` at the same time as component factories. By [@egil](https://github.com/egil). Reported by [BenSchoen](https://github.com/BenSchoen) in . +- Allow using 3rd party `IComponentActivator` at the same time as component factories. By [@egil](https://github.com/egil). Reported by [BenSchoen](https://github.com/BenSchoen) in . -- Calling `IRenderedComponent.Render()` or `IRenderedComponent.SetParametersAndRender()` did not batch up multiple synchronous re-renders after setting parameters. This is now changed such that the method causes the component to re-render with new parameters in the same way as if a parent component had passed new parameters to it. By [@egil](https://github.com/egil). Reported by [@Jcparkyn](https://github.com/Jcparkyn) in . +- Calling `IRenderedComponent.Render()` or `IRenderedComponent.SetParametersAndRender()` did not batch up multiple synchronous re-renders after setting parameters. This is now changed such that the method causes the component to re-render with new parameters in the same way as if a parent component had passed new parameters to it. By [@egil](https://github.com/egil). Reported by [@Jcparkyn](https://github.com/Jcparkyn) in . ## [1.20.8] - 2023-05-21 ### Added -- Added static `DefaultWaitTimeout` property to `TestContext` to enable overriding the default timeout of "wait for" methods like `WaitForAssertion` from 1 second to something else. By [@egil](https://github.com/egil). +- Added static `DefaultWaitTimeout` property to `TestContext` to enable overriding the default timeout of "wait for" methods like `WaitForAssertion` from 1 second to something else. By [@egil](https://github.com/egil). ### Fixed -- TestRenderer throws `ObjectDisposedException` if any methods is accessed after it has been disposed. It will also prevent changes to the internal render tree after it has been disposed. By [@egil](https://github.com/egil). +- TestRenderer throws `ObjectDisposedException` if any methods is accessed after it has been disposed. It will also prevent changes to the internal render tree after it has been disposed. By [@egil](https://github.com/egil). ## [1.19.14] - 2023-04-26 ### Fixed -- Custom elements with attributes throw `ArgumentException` with `MarkupMatches`. Reported by [@candritzky](https://github.com/candritzky). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Custom elements with attributes throw `ArgumentException` with `MarkupMatches`. Reported by [@candritzky](https://github.com/candritzky). Fixed by [@linkdotnet](https://github.com/linkdotnet). ### Changed -- Changed test renderer such that updates to rendered components markup happen in the same synchronization context as the test framework is using (if any), if any, to avoid memory race conditions. By [@egil](https://github.com/egil). +- Changed test renderer such that updates to rendered components markup happen in the same synchronization context as the test framework is using (if any), if any, to avoid memory race conditions. By [@egil](https://github.com/egil). ## [1.18.4] - 2023-02-26 ### Fixed -- Some characters where not properly escaped. Reported by [@pwhe23](https://github.com/pwhe23). Fixed by [@linkdotnet](https://github.com/linkdotnet). -- Clicking a submit button or submit input element inside a form, submits the form, if the submit button or submit input element does not have the `@onclick:preventDefault` attribute set. Reported by [@linkdotnet](https://github.com/linkdotnet). Fixed by [@egil](https://github.com/egil). +- Some characters where not properly escaped. Reported by [@pwhe23](https://github.com/pwhe23). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Clicking a submit button or submit input element inside a form, submits the form, if the submit button or submit input element does not have the `@onclick:preventDefault` attribute set. Reported by [@linkdotnet](https://github.com/linkdotnet). Fixed by [@egil](https://github.com/egil). ## [1.17.2] - 2023-02-22 -- Submit buttons and input fields now no longer cause a form submit when they have the `@onclick:preventDefault` attribute. By [@JelleHissink](https://github.com/JelleHissink). +- Submit buttons and input fields now no longer cause a form submit when they have the `@onclick:preventDefault` attribute. By [@JelleHissink](https://github.com/JelleHissink). ## [1.16.2] - 2023-02-07 -- Changed semantic comparer to handle elements parsed outside their proper context, e.g. an `` element parsed without being inside a `` element. The semantic comparer will now be able to treat those as regular elements and thus be able to compare correctly to other elements of the same type and with the same node name. By [@egil](https://github.com/egil). +- Changed semantic comparer to handle elements parsed outside their proper context, e.g. an `` element parsed without being inside a `` element. The semantic comparer will now be able to treat those as regular elements and thus be able to compare correctly to other elements of the same type and with the same node name. By [@egil](https://github.com/egil). ## [1.15.5] - 2023-02-04 -- Upgrade AngleSharp.Diffing to 0.17.1. +- Upgrade AngleSharp.Diffing to 0.17.1. ## [1.14.4] - 2023-01-11 ### Added -- Added `IMemoryCache` by default to the Services container. By [@linkdotnet](https://github.com/linkdotnet). +- Added `IMemoryCache` by default to the Services container. By [@linkdotnet](https://github.com/linkdotnet). ### Fixed -- Added support in `FakeNavigationManager` to handle umlauts. -- Fixed a bug where attribute values did not get escaped. Reported by [@brettwinters](https://github.com/brettwinters). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Added support in `FakeNavigationManager` to handle umlauts. +- Fixed a bug where attribute values did not get escaped. Reported by [@brettwinters](https://github.com/brettwinters). Fixed by [@linkdotnet](https://github.com/linkdotnet). ## [1.13.5] - 2022-12-16 @@ -147,21 +151,21 @@ This release contains a bunch of small tweaks and fixes. ### Fixed -- The created HTML contained encoded strings. Reported by [@tobiasbrandstaedter](https://github.com/tobiasbrandstaedter). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- The created HTML contained encoded strings. Reported by [@tobiasbrandstaedter](https://github.com/tobiasbrandstaedter). Fixed by [@linkdotnet](https://github.com/linkdotnet). ## [1.11.7] - 2022-10-13 ### Added -- Added the `StateFromJson` method to the `NavigationHistory` type, to make it easy to deserialize navigation state stored as JSON during a call to `NavigationManager.NavigateTo`, e.g. as seen with the new `InteractiveRequestOptions` type available in .NET 7. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). +- Added the `StateFromJson` method to the `NavigationHistory` type, to make it easy to deserialize navigation state stored as JSON during a call to `NavigationManager.NavigateTo`, e.g. as seen with the new `InteractiveRequestOptions` type available in .NET 7. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). ## [1.10.14] - 2022-09-16 ### Added -- Added new test double `FakeWebAssemblyHostEnvironment` that implements `IWebAssemblyHostEnvironment`. By [@KristofferStrube](https://github.com/KristofferStrube). +- Added new test double `FakeWebAssemblyHostEnvironment` that implements `IWebAssemblyHostEnvironment`. By [@KristofferStrube](https://github.com/KristofferStrube). -- Added `Bind` method to parameter builder that makes it easier to emulate the `@bind-Value` syntax in C#-based tests. +- Added `Bind` method to parameter builder that makes it easier to emulate the `@bind-Value` syntax in C#-based tests. When writing tests in razor files, the `@bind-` directive can be directly applied like this: @@ -187,37 +191,37 @@ This release contains a bunch of small tweaks and fixes. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). -- Added support for `NavigationLock`, which allows user code to intercept and prevent navigation. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). +- Added support for `NavigationLock`, which allows user code to intercept and prevent navigation. By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). ### Fixed -- `JSInterop.VerifyInvoke` reported the wrong number of actual invocations of a given identifier. Reported by [@otori](https://github.com/otori). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- `JSInterop.VerifyInvoke` reported the wrong number of actual invocations of a given identifier. Reported by [@otori](https://github.com/otori). Fixed by [@linkdotnet](https://github.com/linkdotnet). ## [1.9.8] - 2022-06-07 ### Changed -- `WaitForAssertion` method is now marked as an assertion method with the `[AssertionMethod]` attribute. This makes certain analyzers like SonarSource's [Tests should include assertions](https://rules.sonarsource.com/csharp/RSPEC-2699) happy. By [@egil](https://github.com/egil). +- `WaitForAssertion` method is now marked as an assertion method with the `[AssertionMethod]` attribute. This makes certain analyzers like SonarSource's [Tests should include assertions](https://rules.sonarsource.com/csharp/RSPEC-2699) happy. By [@egil](https://github.com/egil). ### Fixes -- A race condition existed between `WaitForState` / `WaitForAssertion` and `FindComponents`, if the first used the latter. Reported by [@rmihael](https://github.com/rmihael), [@SviatoslavK](https://github.com/SviatoslavK), and [@RaphaelMarcouxCTRL](https://github.com/RaphaelMarcouxCTRL). Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). +- A race condition existed between `WaitForState` / `WaitForAssertion` and `FindComponents`, if the first used the latter. Reported by [@rmihael](https://github.com/rmihael), [@SviatoslavK](https://github.com/SviatoslavK), and [@RaphaelMarcouxCTRL](https://github.com/RaphaelMarcouxCTRL). Fixed by [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). -- Triggering of event handlers now runs entirely inside the renderers synchronization context, avoiding race condition between elements in the DOM tree being updated by the renderer and the event triggering logic traversing the DOM tree to find event handlers to trigger. Reported by [@FlukeFan](https://github.com/FlukeFan). Fixed by [@egil](https://github.com/egil). +- Triggering of event handlers now runs entirely inside the renderers synchronization context, avoiding race condition between elements in the DOM tree being updated by the renderer and the event triggering logic traversing the DOM tree to find event handlers to trigger. Reported by [@FlukeFan](https://github.com/FlukeFan). Fixed by [@egil](https://github.com/egil). ## [1.8.15] - 2022-05-19 ### Added -- Added test helpers that make it much easier to pass files to the `InputFile` component. Learn more [in the documentation](https://bunit.dev/docs/test-doubles/input-file). By [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). +- Added test helpers that make it much easier to pass files to the `InputFile` component. Learn more [in the documentation](https://bunit.dev/docs/test-doubles/input-file). By [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet). ### Changed -- `Htmlizer` uses `StringBuilder` instead of `List` to reduce allocations and improve render speed. By [@linkdotnet](https://github.com/linkdotnet). +- `Htmlizer` uses `StringBuilder` instead of `List` to reduce allocations and improve render speed. By [@linkdotnet](https://github.com/linkdotnet). ### Fixes -- `TestServiceProvider` now implements `IAsyncDisposable`. This means `TestContext.Dispose()` now calls the async disposable method as well as the non-async version on the service provider. It does however not block or await the task returned, since that can lead to deadlocks. +- `TestServiceProvider` now implements `IAsyncDisposable`. This means `TestContext.Dispose()` now calls the async disposable method as well as the non-async version on the service provider. It does however not block or await the task returned, since that can lead to deadlocks. To await the disposal of async services registered in the `TestContext.Services` container, do the following: @@ -231,17 +235,17 @@ This release contains a bunch of small tweaks and fixes. ### Added -- Added method `SetAuthenticationType` to `TestAuthorizationContext` to allow for custom authentication type checks. By [@TimPurdum](https://github.com/timpurdum). +- Added method `SetAuthenticationType` to `TestAuthorizationContext` to allow for custom authentication type checks. By [@TimPurdum](https://github.com/timpurdum). -- Added `DisposeComponents` to `TestContextBase`. It will dispose and remove all components rendered by the `TestContextBase`. By [@linkdotnet](https://github.com/linkdotnet). +- Added `DisposeComponents` to `TestContextBase`. It will dispose and remove all components rendered by the `TestContextBase`. By [@linkdotnet](https://github.com/linkdotnet). -- Added .NET 7 as a target framework for bUnit. By [@linkdotnet](https://github.com/linkdotnet). +- Added .NET 7 as a target framework for bUnit. By [@linkdotnet](https://github.com/linkdotnet). ### Fixed -- Fixed step by step guide for building and viewing the documentation locally. By [@linkdotnet](https://github.com/linkdotnet). +- Fixed step by step guide for building and viewing the documentation locally. By [@linkdotnet](https://github.com/linkdotnet). -- `FakeNavigationManager.NavigateTo` could lead to exceptions when navigating to external url's. Reported by [@TDroogers](https://github.com/TDroogers). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- `FakeNavigationManager.NavigateTo` could lead to exceptions when navigating to external url's. Reported by [@TDroogers](https://github.com/TDroogers). Fixed by [@linkdotnet](https://github.com/linkdotnet). ## [1.6.4] - 2022-02-22 @@ -249,8 +253,8 @@ A quick minor release that primiarily fixes a regression in 1.5.12. ### Fixed -- `ClickAsync` could lead to bubbling exceptions from `GetDispatchEventTasks` even though they should be handled. Reported by [@aguacongas](aguacongas). Fixed by [@linkdotnet](https://github.com/linkdotnet). -- Added more non bubbling events to bUnit so it behaves closer to the HTML specification. [@linkdotnet](https://github.com/linkdotnet). +- `ClickAsync` could lead to bubbling exceptions from `GetDispatchEventTasks` even though they should be handled. Reported by [@aguacongas](aguacongas). Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Added more non bubbling events to bUnit so it behaves closer to the HTML specification. [@linkdotnet](https://github.com/linkdotnet). ## [1.5.12] - 2022-02-15 @@ -260,29 +264,29 @@ Also a big shout out to **bUnit's sponsors** who helped make this release happen **The higher tier sponsors are:** -- [Progress Telerik](https://github.com/Progress-Telerik) -- [Syncfusion](https://github.com/syncfusion) -- [CTRL Informatique](https://github.com/CTRL-Informatique) +- [Progress Telerik](https://github.com/Progress-Telerik) +- [Syncfusion](https://github.com/syncfusion) +- [CTRL Informatique](https://github.com/CTRL-Informatique) **Other sponsors are:** -- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) -- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) -- [Domn Werner (@domn1995)](https://github.com/domn1995) -- [Mladen Macanović (@stsrki)](https://github.com/stsrki) -- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) -- [Steven Giesel (@linkdotnet)](https://github.com/linkdotnet) +- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) +- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) +- [Domn Werner (@domn1995)](https://github.com/domn1995) +- [Mladen Macanović (@stsrki)](https://github.com/stsrki) +- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) +- [Steven Giesel (@linkdotnet)](https://github.com/linkdotnet) ### Added -- Added `FakeSignOutSessionStateManage` type in Blazor, that makes it easy to test components that use the `SignOutSessionStateManage` type. By [@linkdotnet](https://github.com/linkdotnet). -- Added a validation to `AddChildContent` method in `ComponentParameterCollectionBuilder` that will throw an exception if the component's `ChildContent` is a generic type. By [@denisekart](https://github.com/denisekart). -- Added more optional arguments for `Click` and `DoubleClick` extensions which were introduced in .NET 5 and .NET 6. By [@linkdotnet](https://github.com/linkdotnet). -- Added template support for `Nunit` and `MSTest` unit test frameworks. By [@denisekart](https://github.com/denisekart). +- Added `FakeSignOutSessionStateManage` type in Blazor, that makes it easy to test components that use the `SignOutSessionStateManage` type. By [@linkdotnet](https://github.com/linkdotnet). +- Added a validation to `AddChildContent` method in `ComponentParameterCollectionBuilder` that will throw an exception if the component's `ChildContent` is a generic type. By [@denisekart](https://github.com/denisekart). +- Added more optional arguments for `Click` and `DoubleClick` extensions which were introduced in .NET 5 and .NET 6. By [@linkdotnet](https://github.com/linkdotnet). +- Added template support for `Nunit` and `MSTest` unit test frameworks. By [@denisekart](https://github.com/denisekart). ### Fixed -- Changed `GetDispatchEventTasks` for bubbling events such that handled exceptions are not rethrown later from the `WaitFor...` helpers methods. Reported by [@AndrewStrickland](https://github.com/AndrewStrickland). Fixed by [@linkdotnet](https://github.com/linkdotnet) +- Changed `GetDispatchEventTasks` for bubbling events such that handled exceptions are not rethrown later from the `WaitFor...` helpers methods. Reported by [@AndrewStrickland](https://github.com/AndrewStrickland). Fixed by [@linkdotnet](https://github.com/linkdotnet) ## [1.4.15] - 2021-12-18 @@ -292,28 +296,28 @@ A big shout out to **bUnit's sponsors** who helped make this release happen. **The higher tier sponsors are:** -- [Progress Telerik](https://github.com/Progress-Telerik) -- [Syncfusion](https://github.com/syncfusion) -- [CTRL Informatique](https://github.com/CTRL-Informatique) +- [Progress Telerik](https://github.com/Progress-Telerik) +- [Syncfusion](https://github.com/syncfusion) +- [CTRL Informatique](https://github.com/CTRL-Informatique) **Other sponsors are:** -- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) -- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) -- [Domn Werner (@domn1995)](https://github.com/domn1995) -- [Mladen Macanović (@stsrki)](https://github.com/stsrki) -- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) +- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) +- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) +- [Domn Werner (@domn1995)](https://github.com/domn1995) +- [Mladen Macanović (@stsrki)](https://github.com/stsrki) +- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) ### Added -- Add `ComponentFactories` extensions method that makes it easy to register an instance of a replacement component. By [@egil](https://github.com/egil). -- Add ability to pass `ServiceProviderOptions` to `TestServiceProvider` through property to allow users to customize the service provider. By [@rodolfograve](https://github.com/rodolfograve). +- Add `ComponentFactories` extensions method that makes it easy to register an instance of a replacement component. By [@egil](https://github.com/egil). +- Add ability to pass `ServiceProviderOptions` to `TestServiceProvider` through property to allow users to customize the service provider. By [@rodolfograve](https://github.com/rodolfograve). ### Fixed -- Changed `SetParametersAndRender` such that it rethrows any exceptions thrown by the component under tests `SetParametersAsync` method. Thanks to [@bonsall](https://github.com/bonsall) for reporting the issue. Fixed by [@egil](https://github.com/egil). -- `onclick` on a button inside a form will raise the `onsubmit` event for the form itself. Reported by [@egil]. Fixed by [@linkdotnet](https://github.com/linkdotnet). -- Only forms are allowed to have a `onsubmit` event handler. When `onsubmit` is invoked from a non-form element results in an exception. Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Changed `SetParametersAndRender` such that it rethrows any exceptions thrown by the component under tests `SetParametersAsync` method. Thanks to [@bonsall](https://github.com/bonsall) for reporting the issue. Fixed by [@egil](https://github.com/egil). +- `onclick` on a button inside a form will raise the `onsubmit` event for the form itself. Reported by [@egil]. Fixed by [@linkdotnet](https://github.com/linkdotnet). +- Only forms are allowed to have a `onsubmit` event handler. When `onsubmit` is invoked from a non-form element results in an exception. Fixed by [@linkdotnet](https://github.com/linkdotnet). ## [1.3.42] - 2021-11-09 @@ -323,22 +327,22 @@ Big shout out to **bUnit's sponsors** who helped make this release happen. **The higher tier sponsors are:** -- [Progress Telerik](https://github.com/Progress-Telerik) -- [Syncfusion](https://github.com/syncfusion) +- [Progress Telerik](https://github.com/Progress-Telerik) +- [Syncfusion](https://github.com/syncfusion) **Other sponsors are:** -- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) -- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) -- [Domn Werner (@domn1995)](https://github.com/domn1995) -- [Mladen Macanović (@stsrki)](https://github.com/stsrki) -- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) +- [Hassan Rezk Habib (@hassanhabib)](https://github.com/hassanhabib) +- [Jonny Larsson (@Garderoben)](https://github.com/Garderoben) +- [Domn Werner (@domn1995)](https://github.com/domn1995) +- [Mladen Macanović (@stsrki)](https://github.com/stsrki) +- [@ChristopheDEBOVE](https://github.com/ChristopheDEBOVE) ### Added List of added functionality in this release. -- Added support for writing tests of components that use the `` component included in .NET 6. This includes an assertion helper method `VerifyFocusOnNavigateInvoke` on bUnit's `JSInterop` that allow you to verify that `` has set focus on an element during render. For example, to verify that `h1` selector was used to pick an element to focus on, do: +- Added support for writing tests of components that use the `` component included in .NET 6. This includes an assertion helper method `VerifyFocusOnNavigateInvoke` on bUnit's `JSInterop` that allow you to verify that `` has set focus on an element during render. For example, to verify that `h1` selector was used to pick an element to focus on, do: ```csharp // component uses @@ -353,29 +357,29 @@ List of added functionality in this release. By [@egil](https://github.com/egil). -- Added fake version of the `PersistentComponentState` type in Blazor that makes it possible to test components that use the type. By [@egil](https://github.com/egil). +- Added fake version of the `PersistentComponentState` type in Blazor that makes it possible to test components that use the type. By [@egil](https://github.com/egil). -- Added `TriggerEvent` method to make it easier to trigger custom events. By [@egil](https://github.com/egil). +- Added `TriggerEvent` method to make it easier to trigger custom events. By [@egil](https://github.com/egil). -- Added `History` capture in the `FakeNavigationManager`. By [@egil](https://github.com/egil). +- Added `History` capture in the `FakeNavigationManager`. By [@egil](https://github.com/egil). -- Added new bUnit component mocking library, available via NuGet as `bunit.web.mock`. It is currently in preview and the features/APIs of it will change! +- Added new bUnit component mocking library, available via NuGet as `bunit.web.mock`. It is currently in preview and the features/APIs of it will change! -- Added `WaitForElement` and `WaitForElements` methods. These makes it possible to wait for one or more elements to appear in the DOM before continuing a test, similar to how `WaitForAssertion` allows you to wait for an assertion to pass, or `WaitForState` allows you to wait for a predicate to pass. By [@egil](https://github.com/egil). +- Added `WaitForElement` and `WaitForElements` methods. These makes it possible to wait for one or more elements to appear in the DOM before continuing a test, similar to how `WaitForAssertion` allows you to wait for an assertion to pass, or `WaitForState` allows you to wait for a predicate to pass. By [@egil](https://github.com/egil). ### Changed -- Added automatic conversion of values (types) passed to `Change()` and `Input()` event trigger methods. This means that e.g. a `DateTime` passed to `Change()` is automatically converted to a string format that Blazor expects. By [@egil](https://github.com/egil). +- Added automatic conversion of values (types) passed to `Change()` and `Input()` event trigger methods. This means that e.g. a `DateTime` passed to `Change()` is automatically converted to a string format that Blazor expects. By [@egil](https://github.com/egil). ### Fixed -- The `Click` and `DoubleClick` extension methods now set the `MouseEventArgs.Detail` property to `1` and `2` respectively by default, unless the user specifies something else. This makes the methods more correctly emulate how Blazor reports single or double clicks on an element in the browser. Thanks to [@David-Moreira](https://github.com/David-Moreira) for the help troubleshooting this issue. By [@egil](https://github.com/egil). +- The `Click` and `DoubleClick` extension methods now set the `MouseEventArgs.Detail` property to `1` and `2` respectively by default, unless the user specifies something else. This makes the methods more correctly emulate how Blazor reports single or double clicks on an element in the browser. Thanks to [@David-Moreira](https://github.com/David-Moreira) for the help troubleshooting this issue. By [@egil](https://github.com/egil). -- `FocusAsync()` method handler on `ElementReference` and `` js handler return completed `Task`. By [@anddrzejb](https://github.com/anddrzejb). +- `FocusAsync()` method handler on `ElementReference` and `` js handler return completed `Task`. By [@anddrzejb](https://github.com/anddrzejb). -- Fixes handling of disposed event handlers of bubbling events. See issue [#518](https://github.com/bUnit-dev/bUnit/issues/518) for details. Thanks to [@David-Moreira](https://github.com/David-Moreira) for helping debug this issue. +- Fixes handling of disposed event handlers of bubbling events. See issue [#518](https://github.com/bUnit-dev/bUnit/issues/518) for details. Thanks to [@David-Moreira](https://github.com/David-Moreira) for helping debug this issue. -- Async event trigger methods are not public. In most circumstances you do not need to use them, but if you have a scenario where you want to check that something has not happened after an event handler was triggered, then you can use the async methods and await them to know when they are completed. See [#552](https://github.com/bUnit-dev/bUnit/discussions/552) for details. By [@egil](https://github.com/egil). +- Async event trigger methods are not public. In most circumstances you do not need to use them, but if you have a scenario where you want to check that something has not happened after an event handler was triggered, then you can use the async methods and await them to know when they are completed. See [#552](https://github.com/bUnit-dev/bUnit/discussions/552) for details. By [@egil](https://github.com/egil). ## [1.2.49] - 2021-08-09 @@ -383,50 +387,50 @@ List of added functionality in this release. List of added functionality in this release. -- Added more extensions methods to `MarkupMatchesAssertExtensions` to allow asserting with `MarkupMatches` on `IEnumerable` and `IElement`. By [@jgoday](https://github.com/jgoday). +- Added more extensions methods to `MarkupMatchesAssertExtensions` to allow asserting with `MarkupMatches` on `IEnumerable` and `IElement`. By [@jgoday](https://github.com/jgoday). -- Added `BunitErrorBoundaryLogger` implementation of `IErrorBoundaryLogger` (needed for Blazor's ErrorBoundary component in .NET 6.0). By [@jgoday](https://github.com/jgoday). +- Added `BunitErrorBoundaryLogger` implementation of `IErrorBoundaryLogger` (needed for Blazor's ErrorBoundary component in .NET 6.0). By [@jgoday](https://github.com/jgoday). -- Added `ComponentFactories` property to the `TestContextBase` type. The `ComponentFactories` property is a `ComponentFactoryCollection` type that contains `IComponentFactory` types. These are used by bUnits component activator, whenever a component is created during testing. If no component factories is added to the collection, the standard component activator mechanism from Blazor is used. This feature makes it possible to control what components are created normally during a test, and which should be e.g. replaced by a test dummy. More info is available in issue [#388](https://github.com/bUnit-dev/bUnit/issues/388). +- Added `ComponentFactories` property to the `TestContextBase` type. The `ComponentFactories` property is a `ComponentFactoryCollection` type that contains `IComponentFactory` types. These are used by bUnits component activator, whenever a component is created during testing. If no component factories is added to the collection, the standard component activator mechanism from Blazor is used. This feature makes it possible to control what components are created normally during a test, and which should be e.g. replaced by a test dummy. More info is available in issue [#388](https://github.com/bUnit-dev/bUnit/issues/388). Learn more about this feature on the [Controlling component instantiation](https://bunit.dev/docs/providing-input/controlling-component-instantiation) page. -- Added `HasComponent()` to `IRenderedFragement`. Use it to check if the rendered fragment contains a component of type `TComponent`. Added by [@egil](https://github.com/egil). +- Added `HasComponent()` to `IRenderedFragement`. Use it to check if the rendered fragment contains a component of type `TComponent`. Added by [@egil](https://github.com/egil). -- Added `AddStub` and `Add` extension methods to `ComponentFactories` that makes it easy to configure bUnit to replace components in the render tree with stubs. Both methods have overloads that allow for fine grained selection of component types to "double" during testing. Added by [@egil](https://github.com/egil) in [#400](https://github.com/bUnit-dev/bUnit/pull/400). +- Added `AddStub` and `Add` extension methods to `ComponentFactories` that makes it easy to configure bUnit to replace components in the render tree with stubs. Both methods have overloads that allow for fine grained selection of component types to "double" during testing. Added by [@egil](https://github.com/egil) in [#400](https://github.com/bUnit-dev/bUnit/pull/400). ### Changed List of changes in this release. -- Updated AngleSharp and related libraries to 0.16.0. _NOTE, the new version of AngleSharp includes nullable annotations, which might affect how your code compiles, if you have nullable checking enabled in your test project._ By [@egil](https://github.com/egil). +- Updated AngleSharp and related libraries to 0.16.0. _NOTE, the new version of AngleSharp includes nullable annotations, which might affect how your code compiles, if you have nullable checking enabled in your test project._ By [@egil](https://github.com/egil). -- Updated .NET 6 dependencies to preview 5. By [@egil](https://github.com/egil). +- Updated .NET 6 dependencies to preview 5. By [@egil](https://github.com/egil). ### Fixed List of fixes in this release. -- Fixed JSInterop error message when trying to import an unconfigured module. By [@jgoday](https://github.com/jgoday) in [#425](https://github.com/bUnit-dev/bUnit/pull/425). +- Fixed JSInterop error message when trying to import an unconfigured module. By [@jgoday](https://github.com/jgoday) in [#425](https://github.com/bUnit-dev/bUnit/pull/425). -- Fixed issue where a registered fall-back service provider was not made available to resolve service dependencies of components under test. Thanks to [@dady8889](https://github.com/dady8889) for the reporting the issue. +- Fixed issue where a registered fall-back service provider was not made available to resolve service dependencies of components under test. Thanks to [@dady8889](https://github.com/dady8889) for the reporting the issue. -- Fixed handling of escaped uri's in FakeNavigationManager. By [@linkdotnet](https://github.com/linkdotnet) in [#460](https://github.com/bUnit-dev/bUnit/pull/460). +- Fixed handling of escaped uri's in FakeNavigationManager. By [@linkdotnet](https://github.com/linkdotnet) in [#460](https://github.com/bUnit-dev/bUnit/pull/460). -- Captured error message from event dispatcher in renderer that would previously be hidden from the user. Related to issue [#399](https://github.com/bUnit-dev/bUnit/issues/399). +- Captured error message from event dispatcher in renderer that would previously be hidden from the user. Related to issue [#399](https://github.com/bUnit-dev/bUnit/issues/399). ## [1.1.5] - 2021-04-30 ### Added -- All bUnit assemblies is now strong named signed. +- All bUnit assemblies is now strong named signed. -- Added .NET 6 (preview 3) as a target framework for bUnit, bUnit.core and bUnit.web. +- Added .NET 6 (preview 3) as a target framework for bUnit, bUnit.core and bUnit.web. ### Changed -- Changed bunit.template such that created projects only reference the bUnit package. Bumped other referenced packages to latest version. +- Changed bunit.template such that created projects only reference the bUnit package. Bumped other referenced packages to latest version. -- Changed TestServiceProvider to validate scopes of registered services, such that it behaves like the service provider (default IoC container) in Blazor. +- Changed TestServiceProvider to validate scopes of registered services, such that it behaves like the service provider (default IoC container) in Blazor. ## [1.0.16] @@ -436,12 +440,12 @@ The following section list all changes since preview 02. List of changes in existing functionality. -- _**BREAKING CHANGE:**_ Writing tests using the test components `` and `` components inside .razor files has been moved to its own library, `bunit.web.testcomponents`. This was done for several reasons: +- _**BREAKING CHANGE:**_ Writing tests using the test components `` and `` components inside .razor files has been moved to its own library, `bunit.web.testcomponents`. This was done for several reasons: - - The feature has been experimental since it was introduced, and it was introduced get a more natural way of specifying the component under test and any related markup used by test. - - The feature is only supported with xUnit. - - There are some issues related to the `SourceFileFinder` library, which is used to discover the test components. - - A better way of writing tests in .razor files has been added to bUnit, using _"inline render fragments"_. This method works with all general purpose test frameworks, e.g. MSTest, NUnit, and xUnit, is more flexible, and offer less boilerplate code than the test components. The bUnit documentation has been updated with a guide to this style. + - The feature has been experimental since it was introduced, and it was introduced get a more natural way of specifying the component under test and any related markup used by test. + - The feature is only supported with xUnit. + - There are some issues related to the `SourceFileFinder` library, which is used to discover the test components. + - A better way of writing tests in .razor files has been added to bUnit, using _"inline render fragments"_. This method works with all general purpose test frameworks, e.g. MSTest, NUnit, and xUnit, is more flexible, and offer less boilerplate code than the test components. The bUnit documentation has been updated with a guide to this style. The new package `bunit.web.testcomponents` is provided as is, without expectation of further development or enhancements. If you are using the test components currently for writing tests, it will continue to work for you. If you are starting a new project, or have few of these tests, consider switching to the "inline render fragments" style. @@ -503,7 +507,7 @@ List of changes in existing functionality. List of now removed features. -- The `AddXunitLogger` method, which provided support for capturing `ILogger` messages and passing them to xUnit's `ITestOutputHelper`, has been removed. There were no need to keep xUnit specific code around in bUnit going forward, and there are many implementations on-line that supports this feature, so having it in bUnit made little sense. One such alternative, which bUnit has adopted internally, is to use Serilog. This looks as follows: +- The `AddXunitLogger` method, which provided support for capturing `ILogger` messages and passing them to xUnit's `ITestOutputHelper`, has been removed. There were no need to keep xUnit specific code around in bUnit going forward, and there are many implementations on-line that supports this feature, so having it in bUnit made little sense. One such alternative, which bUnit has adopted internally, is to use Serilog. This looks as follows: 1. Add the following packages to your test project: `Serilog`, `Serilog.Extensions.Logging`, and `Serilog.Sinks.XUnit`. 2. Add the following class/extension method to your test project (which replicates the signature of the removed `AddXunitLogger` method): @@ -535,7 +539,7 @@ List of now removed features. } ``` -- The `bunit.xunit` package has been removed, since it is no longer needed (there is no code left in it). +- The `bunit.xunit` package has been removed, since it is no longer needed (there is no code left in it). ## [1.0.0-preview-02] - 2021-03-26 @@ -547,9 +551,9 @@ The plan is to make this the last preview release of bUnit. If no big blocking b List of new features. -- Added the ability to pass a "fallback `IServiceProvider`" to the `TestServiceProvider`, available through the `Services` property on a `TestContext`. The fallback service provider enables a few interesting scenarios, such as using an alternative IoC container, or automatically generating mocks of services components under test depend on. See the [Injecting Services into Components Under Test page](https://bunit.egilhansen.com/docs/providing-input/inject-services-into-components) for more details on this feature. By [@thopdev](https://github.com/thopdev) in [#310](https://github.com/egil/bUnit/issues/310). +- Added the ability to pass a "fallback `IServiceProvider`" to the `TestServiceProvider`, available through the `Services` property on a `TestContext`. The fallback service provider enables a few interesting scenarios, such as using an alternative IoC container, or automatically generating mocks of services components under test depend on. See the [Injecting Services into Components Under Test page](https://bunit.egilhansen.com/docs/providing-input/inject-services-into-components) for more details on this feature. By [@thopdev](https://github.com/thopdev) in [#310](https://github.com/egil/bUnit/issues/310). -- Added `Task ITestRenderer.UnhandledException` property that returns a `Task` that completes when the renderer captures an unhandled exception from a component under test. If a component is missing exception handling of asynchronous operations, e.g. in the `OnInitializedAsync` method, the exception will not break the test, because it happens on another thread. To have a test fail in this scenario, you can await the `UnhandledException` property on the `TestContext.Renderer` property, e.g.: +- Added `Task ITestRenderer.UnhandledException` property that returns a `Task` that completes when the renderer captures an unhandled exception from a component under test. If a component is missing exception handling of asynchronous operations, e.g. in the `OnInitializedAsync` method, the exception will not break the test, because it happens on another thread. To have a test fail in this scenario, you can await the `UnhandledException` property on the `TestContext.Renderer` property, e.g.: ```csharp using var ctx = new TestContext(); @@ -568,7 +572,7 @@ List of new features. By [@egil](https://github.com/egil) in [#344](https://github.com/egil/bUnit/issues/344). -- Added a simple fake navigation manager, which is registered by default in bUnit's service provider. When the fake navigation manager's `NavigateTo` method is called, it does two things: +- Added a simple fake navigation manager, which is registered by default in bUnit's service provider. When the fake navigation manager's `NavigateTo` method is called, it does two things: 1. Set the `Uri` property to the URI passed to the `NavigateTo` method (with the URI normalized to an absolute URI). 2. Raise the `LocationChanged` event with the URI passed to the `NavigateTo` method. @@ -607,15 +611,15 @@ List of new features. By [@egil](https://github.com/egil) in [#345](https://github.com/egil/bUnit/pull/345). -- Added additional bUnit JSInterop `Setup` methods, that makes it possible to get complete control of invocation matching for the created handler. By [@egil](https://github.com/egil). +- Added additional bUnit JSInterop `Setup` methods, that makes it possible to get complete control of invocation matching for the created handler. By [@egil](https://github.com/egil). ### Changed List of changes in existing functionality. -- `WaitForAssertion` and `WaitForState` now throws unhandled exception caught by the renderer from a component under test. This can happen if a component is awaiting an asynchronous operation that throws, e.g. a API call using a misconfigured `HttpClient`. By [@egil](https://github.com/egil) in [#310](https://github.com/egil/bUnit/issues/344). +- `WaitForAssertion` and `WaitForState` now throws unhandled exception caught by the renderer from a component under test. This can happen if a component is awaiting an asynchronous operation that throws, e.g. a API call using a misconfigured `HttpClient`. By [@egil](https://github.com/egil) in [#310](https://github.com/egil/bUnit/issues/344). -- Improvements to error message from bUnit's JSInterop when it receives an invocation that it has not been set up to handle. By [@egil](https://github.com/egil) in [#346](https://github.com/egil/bUnit/pull/346). +- Improvements to error message from bUnit's JSInterop when it receives an invocation that it has not been set up to handle. By [@egil](https://github.com/egil) in [#346](https://github.com/egil/bUnit/pull/346). ### Removed @@ -633,13 +637,13 @@ The following section list all changes in 1.0.0 preview 01. List of new features. -- Added support for casting `BUnitJSRuntime` to `IJSInProcessRuntime` and `IJSUnmarshalledRuntime`. By [@KristofferStrube](https://github.com/KristofferStrube) in [#279](https://github.com/egil/bUnit/pull/279) +- Added support for casting `BUnitJSRuntime` to `IJSInProcessRuntime` and `IJSUnmarshalledRuntime`. By [@KristofferStrube](https://github.com/KristofferStrube) in [#279](https://github.com/egil/bUnit/pull/279) -- Added support for triggering `@ontoggle` event handlers through a dedicated `Toggle()` method. By [@egil](https://github.com/egil) in [#256](https://github.com/egil/bUnit/pull/256). +- Added support for triggering `@ontoggle` event handlers through a dedicated `Toggle()` method. By [@egil](https://github.com/egil) in [#256](https://github.com/egil/bUnit/pull/256). -- Added out of the box support for `` component. When a `` component is used in a component under test, it's JavaScript interop-calls are faked by bUnits JSInterop, and it should result in all items being rendered immediately. By [@egil](https://github.com/egil) in [#240](https://github.com/egil/bUnit/issues/240). +- Added out of the box support for `` component. When a `` component is used in a component under test, it's JavaScript interop-calls are faked by bUnits JSInterop, and it should result in all items being rendered immediately. By [@egil](https://github.com/egil) in [#240](https://github.com/egil/bUnit/issues/240). -- Added support for components that call `ElementReference.FocusAsync`. These calls are handled by the bUnits JSInterop, that also allows you to verify that `FocusAsync` has been called for a specific element. For example, if a component has rendered an ` + + title + + + + + + + + """); + + actual.Length.ShouldBe(2); + actual[0].ShouldBeAssignableTo(); + actual[1].ShouldBeAssignableTo(); + } + private static void VerifyElementParsedWithId(string expectedElementName, List actual) { var elm = actual.OfType() From 5e188a59551a658912a5370d8e367046a9b76e95 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Fri, 5 Apr 2024 09:25:53 +0000 Subject: [PATCH 15/31] fix: enable samples to build .net 9 --- docs/samples/components/bunit.docs.samples.csproj | 12 ++++++++++-- docs/samples/tests/Directory.Build.props | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/samples/components/bunit.docs.samples.csproj b/docs/samples/components/bunit.docs.samples.csproj index 59724ee34..aa97c0fe7 100644 --- a/docs/samples/components/bunit.docs.samples.csproj +++ b/docs/samples/components/bunit.docs.samples.csproj @@ -1,8 +1,8 @@ - netstandard2.0;net5.0;net6.0;net7.0;net8.0 - 10.0 + netstandard2.0;net5.0;net6.0;net7.0;net8.0;net9.0 + latest 3.0 Bunit.Docs.Samples enable @@ -50,4 +50,12 @@ + + + + + + + + diff --git a/docs/samples/tests/Directory.Build.props b/docs/samples/tests/Directory.Build.props index 97c80b32e..8422a0457 100644 --- a/docs/samples/tests/Directory.Build.props +++ b/docs/samples/tests/Directory.Build.props @@ -1,6 +1,6 @@ - netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 + netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0 false true false @@ -16,6 +16,10 @@ + + + + From ff723aa9fd83b568e7b262bb7bf682b5172ea30f Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Tue, 9 Apr 2024 09:56:05 +0000 Subject: [PATCH 16/31] docs: escape doctype html to avoid breaking nuget package build that includes release notes --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e15c7049..77148ffe6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to **bUnit** will be documented in this file. The project ad ### Fixed -- bUnit's built-in HTML parser did not correctly parse full HTML documents that included a `` as the first element. Fixed by [@egil](https://github.com/egil). +- bUnit's built-in HTML parser did not correctly parse full HTML documents that included a <!DOCTYPE html> as the first element. Fixed by [@egil](https://github.com/egil). ### Changed - `NavigationManager` is again registered as a singleton instead of scoped. From a508f94e9b92159cf4668a74726912890ed12c64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 09:59:45 +0000 Subject: [PATCH 17/31] build(deps): Bump thomaseizinger/keep-a-changelog-new-release (#1437) Bumps [thomaseizinger/keep-a-changelog-new-release](https://github.com/thomaseizinger/keep-a-changelog-new-release) from 2.0.0 to 3.0.0. - [Release notes](https://github.com/thomaseizinger/keep-a-changelog-new-release/releases) - [Changelog](https://github.com/thomaseizinger/keep-a-changelog-new-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/thomaseizinger/keep-a-changelog-new-release/compare/2.0.0...3.0.0) --- updated-dependencies: - dependency-name: thomaseizinger/keep-a-changelog-new-release dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9fd8108cf..8eb203e0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: 9.0.x - name: 🛠️ Update changelog - uses: thomaseizinger/keep-a-changelog-new-release@2.0.0 + uses: thomaseizinger/keep-a-changelog-new-release@3.0.0 with: version: ${{ env.NBGV_SemVer2 }} From 6b81d867b8ca9ac9a780e1a0d1148541fec71772 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Tue, 9 Apr 2024 10:09:43 +0000 Subject: [PATCH 18/31] ci: use local tools defined in .config/dotnet-tool.json to build docs --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 341efd5e6..daa6d708f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,9 +236,9 @@ jobs: - name: 📄 Build docs working-directory: ./docs/site run: | - dotnet tool install --global docfx --version 2.74.1 - docfx metadata --logLevel error - docfx build --logLevel warning --warningsAsErrors + dotnet tool restore + dotnet docfx metadata --logLevel error + dotnet docfx build --logLevel warning --warningsAsErrors dependency-review: runs-on: ubuntu-latest From da72b953768970a2d8162549338eba97581ed4d1 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Tue, 9 Apr 2024 10:19:24 +0000 Subject: [PATCH 19/31] fix: enable docs sample to build --- docs/samples/.editorconfig | 4 +++- .../TestDoubles/Components/ComponentDoubleBase{TComponent}.cs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/samples/.editorconfig b/docs/samples/.editorconfig index df7223753..321f69c96 100644 --- a/docs/samples/.editorconfig +++ b/docs/samples/.editorconfig @@ -17,9 +17,11 @@ tab_size = 2 indent_style = space indent_size = 2 indent_width = 2 + dotnet_diagnostic.BL0001.severity = none dotnet_diagnostic.BL0002.severity = none dotnet_diagnostic.BL0003.severity = none dotnet_diagnostic.BL0004.severity = none dotnet_diagnostic.BL0005.severity = none -dotnet_diagnostic.BL0006.severity = none \ No newline at end of file +dotnet_diagnostic.BL0006.severity = none +dotnet_diagnostic.BL0007.severity = none \ No newline at end of file diff --git a/src/bunit.web/TestDoubles/Components/ComponentDoubleBase{TComponent}.cs b/src/bunit.web/TestDoubles/Components/ComponentDoubleBase{TComponent}.cs index a97225e68..9cb924278 100644 --- a/src/bunit.web/TestDoubles/Components/ComponentDoubleBase{TComponent}.cs +++ b/src/bunit.web/TestDoubles/Components/ComponentDoubleBase{TComponent}.cs @@ -20,8 +20,7 @@ public abstract class ComponentDoubleBase : IComponent /// that this stub replaced in the component tree. /// [Parameter(CaptureUnmatchedValues = true)] - public CapturedParameterView Parameters { get; private set; } - = CapturedParameterView.Empty; + public CapturedParameterView Parameters { get; set; } = CapturedParameterView.Empty; /// public virtual Task SetParametersAsync(ParameterView parameters) From 0b71d37f89e9222f674a0d278ae57ac6731c1ce4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:19:52 +0000 Subject: [PATCH 20/31] build(deps): Bump SonarAnalyzer.CSharp from 9.22.0.87781 to 9.23.1.88495 (#1431) Bumps [SonarAnalyzer.CSharp](https://github.com/SonarSource/sonar-dotnet) from 9.22.0.87781 to 9.23.1.88495. - [Release notes](https://github.com/SonarSource/sonar-dotnet/releases) - [Commits](https://github.com/SonarSource/sonar-dotnet/compare/9.22.0.87781...9.23.1.88495) --- updated-dependencies: - dependency-name: SonarAnalyzer.CSharp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8ffa9d5b9..750f5a815 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,7 +52,7 @@ - + Date: Tue, 9 Apr 2024 10:31:03 +0000 Subject: [PATCH 21/31] build(deps): Bump Verify.Xunit from 23.5.2 to 23.7.2 (#1439) Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 23.5.2 to 23.7.2. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/23.5.2...23.7.2) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/bunit.generators.tests/bunit.generators.tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index fbbd5c3c5..7231e6f7a 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -17,7 +17,7 @@ - + From d8ea6e38cf867eb9e9977fb0c5ade7c0cc215149 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:31:18 +0000 Subject: [PATCH 22/31] build(deps): Bump docfx from 2.75.3 to 2.76.0 (#1436) Bumps [docfx](https://github.com/dotnet/docfx) from 2.75.3 to 2.76.0. - [Release notes](https://github.com/dotnet/docfx/releases) - [Changelog](https://github.com/dotnet/docfx/blob/main/RELEASENOTE.md) - [Commits](https://github.com/dotnet/docfx/compare/v2.75.3...v2.76.0) --- updated-dependencies: - dependency-name: docfx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index b592b0e01..f5c8642b9 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "docfx": { - "version": "2.75.3", + "version": "2.76.0", "commands": [ "docfx" ] From 69f866ef9f0d8d48594418411f8e908172a62abe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:31:28 +0000 Subject: [PATCH 23/31] build(deps): Bump dotnet-serve from 1.10.172 to 1.10.175 (#1430) Bumps [dotnet-serve](https://github.com/natemcmaster/dotnet-serve) from 1.10.172 to 1.10.175. - [Release notes](https://github.com/natemcmaster/dotnet-serve/releases) - [Commits](https://github.com/natemcmaster/dotnet-serve/compare/v1.10.172...v1.10.175) --- updated-dependencies: - dependency-name: dotnet-serve dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index f5c8642b9..b3e2a1dff 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-serve": { - "version": "1.10.172", + "version": "1.10.175", "commands": [ "dotnet-serve" ] From 60699a36039bd31d11dd4182c0d9cf1fde26406d Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Tue, 9 Apr 2024 10:52:50 +0000 Subject: [PATCH 24/31] chore: remove old code --- bunit.sln | 1 + docs/site/Program.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 docs/site/Program.cs diff --git a/bunit.sln b/bunit.sln index 89b8e8094..4ce0606e1 100644 --- a/bunit.sln +++ b/bunit.sln @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".items", ".items", "{A5D7B6 .gitattributes = .gitattributes .gitignore = .gitignore Directory.Build.props = Directory.Build.props + .config\dotnet-tools.json = .config\dotnet-tools.json global.json = global.json version.json = version.json EndProjectSection diff --git a/docs/site/Program.cs b/docs/site/Program.cs deleted file mode 100644 index 82b12807f..000000000 --- a/docs/site/Program.cs +++ /dev/null @@ -1 +0,0 @@ -await Docfx.Docset.Build("docfx.json"); \ No newline at end of file From cbe0cbd2a8671bc5733dc8ffd64b98522c565478 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Fri, 12 Apr 2024 17:15:42 +0000 Subject: [PATCH 25/31] ci: add automatic rebase of v2 on main after push to main --- .github/workflows/rebase-v2-on-main.yml | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/rebase-v2-on-main.yml diff --git a/.github/workflows/rebase-v2-on-main.yml b/.github/workflows/rebase-v2-on-main.yml new file mode 100644 index 000000000..236edf253 --- /dev/null +++ b/.github/workflows/rebase-v2-on-main.yml @@ -0,0 +1,57 @@ +on: + push: + branches: + - main + + workflow_dispatch: + +env: + VSTEST_CONNECTION_TIMEOUT: 180 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_NOLOGO: true + DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1 + TERM: xterm + +jobs: + rebase: + name: 🎁 Rebase V2 on Main + runs-on: ubuntu-latest + steps: + + - name: 🛒 Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.BUNIT_BOT_TOKEN }} + + - name: ⚙️ Import GPG key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.BUNIT_BOT_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.BUNIT_BOT_GPG_KEY_PASSPHRASE }} + + - name: ⚙️ Setup CI GIT + run: | + git config user.name "${{ steps.import_gpg.outputs.name }}" + git config user.email ${{ steps.import_gpg.outputs.email }} + git config --global user.signingkey ${{ steps.import_gpg.outputs.keyid }} + git config --global commit.gpgsign true + + - name: ⏩ Merge stable with main, push to origin + id: rebaseV2 + continue-on-error: true + run: | + git checkout v2 + git git rebase origin/main + git push origin v2 + + - name: ⏭ Create pull request from main to v2 when direct rebase fails + if: steps.rebaseV2.outcome == 'failure' + uses: thomaseizinger/create-pull-request@1.3.1 + env: + GITHUB_TOKEN: ${{ secrets.BUNIT_BOT_TOKEN }} + with: + head: main + base: v2 + title: Rebase v2 on main From 1e3bd94154ae38eab5415f4e52eee766cda611df Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Fri, 12 Apr 2024 17:19:02 +0000 Subject: [PATCH 26/31] ci: fix mistake in rebase workflow --- .github/workflows/rebase-v2-on-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rebase-v2-on-main.yml b/.github/workflows/rebase-v2-on-main.yml index 236edf253..875e97259 100644 --- a/.github/workflows/rebase-v2-on-main.yml +++ b/.github/workflows/rebase-v2-on-main.yml @@ -43,7 +43,7 @@ jobs: continue-on-error: true run: | git checkout v2 - git git rebase origin/main + git rebase origin/main git push origin v2 - name: ⏭ Create pull request from main to v2 when direct rebase fails From 05c7564ddf5509a35b339954fb1054fbd103d2ff Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Fri, 12 Apr 2024 17:29:19 +0000 Subject: [PATCH 27/31] ci: disable automatic rebase attempt --- .github/workflows/rebase-v2-on-main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rebase-v2-on-main.yml b/.github/workflows/rebase-v2-on-main.yml index 875e97259..dca6da5c6 100644 --- a/.github/workflows/rebase-v2-on-main.yml +++ b/.github/workflows/rebase-v2-on-main.yml @@ -43,6 +43,7 @@ jobs: continue-on-error: true run: | git checkout v2 + git fetch git rebase origin/main git push origin v2 From f8ede161e124397f49484df0d6a0f8f7c4e8451c Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Fri, 12 Apr 2024 17:55:15 +0000 Subject: [PATCH 28/31] ci: tweak rebase of v2 on main script --- .github/workflows/rebase-v2-on-main.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/rebase-v2-on-main.yml b/.github/workflows/rebase-v2-on-main.yml index dca6da5c6..714b3da5b 100644 --- a/.github/workflows/rebase-v2-on-main.yml +++ b/.github/workflows/rebase-v2-on-main.yml @@ -1,3 +1,5 @@ +name: rebase-v2-on-main + on: push: branches: @@ -6,10 +8,6 @@ on: workflow_dispatch: env: - VSTEST_CONNECTION_TIMEOUT: 180 - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - DOTNET_NOLOGO: true - DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1 TERM: xterm jobs: @@ -38,16 +36,16 @@ jobs: git config --global user.signingkey ${{ steps.import_gpg.outputs.keyid }} git config --global commit.gpgsign true - - name: ⏩ Merge stable with main, push to origin + - name: ⏩ Rebase v2 on main, push to origin id: rebaseV2 continue-on-error: true run: | + git fetch --all git checkout v2 - git fetch - git rebase origin/main - git push origin v2 + git rebase -S main + git push --force-with-lease - - name: ⏭ Create pull request from main to v2 when direct rebase fails + - name: ⏭ Create pull request if: steps.rebaseV2.outcome == 'failure' uses: thomaseizinger/create-pull-request@1.3.1 env: @@ -56,3 +54,7 @@ jobs: head: main base: v2 title: Rebase v2 on main + body: | + This PR is created automatically by the bUnit bot. + + When completing this PR, it's important to use **Rebase and merge** to keep the commit history clean. From 27a43f76b0ede9020f357e2d713205480ca8ccef Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Fri, 19 Apr 2024 16:26:51 +0200 Subject: [PATCH 29/31] fix: Htmlizer can handle NamedEvents Fixes #1438 --- CHANGELOG.md | 2 ++ src/bunit.web/Rendering/Internal/Htmlizer.cs | 4 ++++ .../SampleComponents/FormNameComponent.razor | 3 +++ .../Rendering/Internal/HtmlizerTests.cs | 14 ++++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 tests/bunit.testassets/SampleComponents/FormNameComponent.razor diff --git a/CHANGELOG.md b/CHANGELOG.md index 77148ffe6..6776b28a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ All notable changes to **bUnit** will be documented in this file. The project ad ### Fixed - bUnit's built-in HTML parser did not correctly parse full HTML documents that included a <!DOCTYPE html> as the first element. Fixed by [@egil](https://github.com/egil). +- `@formname` directive led to an `InvalidOperationException` when used on a form element. Reported by [@suzu2469](https://github.com/suzu2469) in [#1438](https://github.com/bUnit-dev/bUnit/issues/1438). + Fixed by [@egil](https://github.com/egil)/[@linkdotnet](https://github.com/linkdotnet). ### Changed - `NavigationManager` is again registered as a singleton instead of scoped. diff --git a/src/bunit.web/Rendering/Internal/Htmlizer.cs b/src/bunit.web/Rendering/Internal/Htmlizer.cs index 9db5e9892..24c42b7c7 100644 --- a/src/bunit.web/Rendering/Internal/Htmlizer.cs +++ b/src/bunit.web/Rendering/Internal/Htmlizer.cs @@ -119,6 +119,10 @@ int position case RenderTreeFrameType.ElementReferenceCapture: case RenderTreeFrameType.ComponentReferenceCapture: return position + 1; +#if NET8_0_OR_GREATER + case RenderTreeFrameType.NamedEvent: + return position + 1; +#endif default: throw new InvalidOperationException( $"Invalid element frame type '{frame.FrameType}'." diff --git a/tests/bunit.testassets/SampleComponents/FormNameComponent.razor b/tests/bunit.testassets/SampleComponents/FormNameComponent.razor new file mode 100644 index 000000000..cd3ad3c4a --- /dev/null +++ b/tests/bunit.testassets/SampleComponents/FormNameComponent.razor @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/tests/bunit.web.tests/Rendering/Internal/HtmlizerTests.cs b/tests/bunit.web.tests/Rendering/Internal/HtmlizerTests.cs index d626b86a7..ee2519a85 100644 --- a/tests/bunit.web.tests/Rendering/Internal/HtmlizerTests.cs +++ b/tests/bunit.web.tests/Rendering/Internal/HtmlizerTests.cs @@ -39,7 +39,21 @@ public void Test003() cut.Find("button").HasAttribute("blazor:elementreference").ShouldBeTrue(); } + +#if NET8_0_OR_GREATER + [Fact(DisplayName = "Htmlizer ignores NamedEvents")] + public void Test004() + { + var cut = RenderComponent(); + cut.MarkupMatches(""" +
+ +
+ """); + } +#endif + private sealed class Htmlizer01Component : ComponentBase { public ElementReference ButtomElmRef { get; set; } From afadabda53823f8502b38c77fb10c29bc7dea07e Mon Sep 17 00:00:00 2001 From: bUnit bot Date: Fri, 19 Apr 2024 14:29:52 +0000 Subject: [PATCH 30/31] Set version to '1.28' --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index a3427a8f8..661a20fe9 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.28-preview", + "version": "1.28", "assemblyVersion": { "precision": "revision" }, From f246f7acf8dcb643f060d2cb90b43c39c899bcfd Mon Sep 17 00:00:00 2001 From: bUnit bot Date: Fri, 19 Apr 2024 14:42:46 +0000 Subject: [PATCH 31/31] Updated CHANGELOG.md for 1.28.9 release --- CHANGELOG.md | 705 +++++++++++++++++++++++++-------------------------- 1 file changed, 341 insertions(+), 364 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6776b28a6..e23662bae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,16 @@ All notable changes to **bUnit** will be documented in this file. The project ad ## [Unreleased] +## [1.28.9] - 2024-04-19 + ### Fixed -- bUnit's built-in HTML parser did not correctly parse full HTML documents that included a <!DOCTYPE html> as the first element. Fixed by [@egil](https://github.com/egil). +- bUnit's built-in HTML parser did not correctly parse full HTML documents that included a <!DOCTYPE html> as the first element. Fixed by [@egil](https://github.com/egil). - `@formname` directive led to an `InvalidOperationException` when used on a form element. Reported by [@suzu2469](https://github.com/suzu2469) in [#1438](https://github.com/bUnit-dev/bUnit/issues/1438). Fixed by [@egil](https://github.com/egil)/[@linkdotnet](https://github.com/linkdotnet). ### Changed + - `NavigationManager` is again registered as a singleton instead of scoped. ## [1.27.17] - 2024-03-02 @@ -171,25 +174,25 @@ This release contains a bunch of small tweaks and fixes. When writing tests in razor files, the `@bind-` directive can be directly applied like this: - ```razor - - ``` + ```razor + + ``` The same expression in C# syntax is more verbose like this: - ```csharp - RenderComponent(ps => ps - .Add(c => c.Value, value) - .Add(c => c.ValueChanged, newValue => value = newValue) - .Add(c => c.ValueExpression, () => value)); - ``` + ```csharp + RenderComponent(ps => ps + .Add(c => c.Value, value) + .Add(c => c.ValueChanged, newValue => value = newValue) + .Add(c => c.ValueExpression, () => value)); + ``` With the new `Bind` method this can be done in one method: - ```csharp - RenderComponent(ps => ps - .Bind(c => c.Value, value, newValue => value = newValue, () => value)); - ``` + ```csharp + RenderComponent(ps => ps + .Bind(c => c.Value, value, newValue => value = newValue, () => value)); + ``` By [@linkdotnet](https://github.com/linkdotnet) and [@egil](https://github.com/egil). @@ -227,11 +230,11 @@ This release contains a bunch of small tweaks and fixes. To await the disposal of async services registered in the `TestContext.Services` container, do the following: - 1. Create a new type that derives from `TestContext` and which implement `IAsyncDisposable`. - 2. In the `DisposeAsync()` method, call `Services.DisposeAsync()`. - 3. Override the `Dispose` and have it only call `Services.Dispose()`. + 1. Create a new type that derives from `TestContext` and which implement `IAsyncDisposable`. + 2. In the `DisposeAsync()` method, call `Services.DisposeAsync()`. + 3. Override the `Dispose` and have it only call `Services.Dispose()`. - Reported by [@vedion](https://github.com/vedion) and fixed by [@egil](https://github.com/egil). + Reported by [@vedion](https://github.com/vedion) and fixed by [@egil](https://github.com/egil). ## [1.7.7] - 2022-04-29 @@ -346,16 +349,16 @@ List of added functionality in this release. - Added support for writing tests of components that use the `` component included in .NET 6. This includes an assertion helper method `VerifyFocusOnNavigateInvoke` on bUnit's `JSInterop` that allow you to verify that `` has set focus on an element during render. For example, to verify that `h1` selector was used to pick an element to focus on, do: - ```csharp - // component uses - var cut = RenderComponent(); + ```csharp + // component uses + var cut = RenderComponent(); - // Verifies that called it's JavaScript function - var invocation = JSInterop.VerifyFocusOnNavigateInvoke(); + // Verifies that called it's JavaScript function + var invocation = JSInterop.VerifyFocusOnNavigateInvoke(); - // Verify that the invocation of JavaScript function included the "h1" as the selector - Assert.Equal("h1", invocation.Arguments[0]); - ``` + // Verify that the invocation of JavaScript function included the "h1" as the selector + Assert.Equal("h1", invocation.Arguments[0]); + ``` By [@egil](https://github.com/egil). @@ -444,66 +447,66 @@ List of changes in existing functionality. - _**BREAKING CHANGE:**_ Writing tests using the test components `` and `` components inside .razor files has been moved to its own library, `bunit.web.testcomponents`. This was done for several reasons: - - The feature has been experimental since it was introduced, and it was introduced get a more natural way of specifying the component under test and any related markup used by test. - - The feature is only supported with xUnit. - - There are some issues related to the `SourceFileFinder` library, which is used to discover the test components. - - A better way of writing tests in .razor files has been added to bUnit, using _"inline render fragments"_. This method works with all general purpose test frameworks, e.g. MSTest, NUnit, and xUnit, is more flexible, and offer less boilerplate code than the test components. The bUnit documentation has been updated with a guide to this style. - - The new package `bunit.web.testcomponents` is provided as is, without expectation of further development or enhancements. If you are using the test components currently for writing tests, it will continue to work for you. If you are starting a new project, or have few of these tests, consider switching to the "inline render fragments" style. - - Here is a quick comparison of the styles, using a very simple component. - - First, the test component style: - - ```razor - @inherits TestComponentBase - - - - - - - @code - { - void HelloWorldComponentRendersCorrectly(Fixture fixture) - { - // Act - var cut = fixture.GetComponentUnderTest(); - - // Assert - cut.MarkupMatches("

Hello world from Blazor

"); - } - } -
- - - - - - -

Hello world from Blazor

-
-
- ``` - - The a single test in "inline render fragments" style covers both cases: - - @inherits TestContext - @code { - [Fact] - public void HelloWorldComponentRendersCorrectly() - { - // Act - var cut = Render(@); - - // Assert - cut.MarkupMatches(@

Hello world from Blazor

); - } + - The feature has been experimental since it was introduced, and it was introduced get a more natural way of specifying the component under test and any related markup used by test. + - The feature is only supported with xUnit. + - There are some issues related to the `SourceFileFinder` library, which is used to discover the test components. + - A better way of writing tests in .razor files has been added to bUnit, using _"inline render fragments"_. This method works with all general purpose test frameworks, e.g. MSTest, NUnit, and xUnit, is more flexible, and offer less boilerplate code than the test components. The bUnit documentation has been updated with a guide to this style. + + The new package `bunit.web.testcomponents` is provided as is, without expectation of further development or enhancements. If you are using the test components currently for writing tests, it will continue to work for you. If you are starting a new project, or have few of these tests, consider switching to the "inline render fragments" style. + + Here is a quick comparison of the styles, using a very simple component. + + First, the test component style: + + ```razor + @inherits TestComponentBase + + + + + + + @code + { + void HelloWorldComponentRendersCorrectly(Fixture fixture) + { + // Act + var cut = fixture.GetComponentUnderTest(); + + // Assert + cut.MarkupMatches("

Hello world from Blazor

"); + } + } +
+ + + + + + +

Hello world from Blazor

+
+
+ ``` + + The a single test in "inline render fragments" style covers both cases: + + @inherits TestContext + @code { + [Fact] + public void HelloWorldComponentRendersCorrectly() + { + // Act + var cut = Render(@); + + // Assert + cut.MarkupMatches(@

Hello world from Blazor

); } + } - To make the snapshot test scenario even more compact, consider putting all code in one line, e.g. `Render(@).MarkupMatches(@

Hello world from Blazor

);`. + To make the snapshot test scenario even more compact, consider putting all code in one line, e.g. `Render(@).MarkupMatches(@

Hello world from Blazor

);`. - For a more complete snapshot testing experience, I recommend looking at Simon Cropp's [Verify](https://github.com/VerifyTests) library, in particular the [Verify.Blazor extension to bUnit](https://github.com/VerifyTests/Verify.Blazor#verifybunit). Verify comes with all the features you expect from a snapshot testing library. + For a more complete snapshot testing experience, I recommend looking at Simon Cropp's [Verify](https://github.com/VerifyTests) library, in particular the [Verify.Blazor extension to bUnit](https://github.com/VerifyTests/Verify.Blazor#verifybunit). Verify comes with all the features you expect from a snapshot testing library. ### Removed @@ -511,35 +514,35 @@ List of now removed features. - The `AddXunitLogger` method, which provided support for capturing `ILogger` messages and passing them to xUnit's `ITestOutputHelper`, has been removed. There were no need to keep xUnit specific code around in bUnit going forward, and there are many implementations on-line that supports this feature, so having it in bUnit made little sense. One such alternative, which bUnit has adopted internally, is to use Serilog. This looks as follows: - 1. Add the following packages to your test project: `Serilog`, `Serilog.Extensions.Logging`, and `Serilog.Sinks.XUnit`. - 2. Add the following class/extension method to your test project (which replicates the signature of the removed `AddXunitLogger` method): - - ```csharp - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using Serilog; - using Serilog.Events; - using Xunit.Abstractions; - - namespace Bunit - { - public static class ServiceCollectionLoggingExtensions - { - public static IServiceCollection AddXunitLogger(this IServiceCollection services, ITestOutputHelper outputHelper) - { - var serilogLogger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper, LogEventLevel.Verbose) - .CreateLogger(); - - services.AddSingleton(new LoggerFactory().AddSerilog(serilogLogger, dispose: true)); - services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); - - return services; - } - } - } - ``` + 1. Add the following packages to your test project: `Serilog`, `Serilog.Extensions.Logging`, and `Serilog.Sinks.XUnit`. + 2. Add the following class/extension method to your test project (which replicates the signature of the removed `AddXunitLogger` method): + + ```csharp + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using Serilog; + using Serilog.Events; + using Xunit.Abstractions; + + namespace Bunit + { + public static class ServiceCollectionLoggingExtensions + { + public static IServiceCollection AddXunitLogger(this IServiceCollection services, ITestOutputHelper outputHelper) + { + var serilogLogger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo.TestOutput(outputHelper, LogEventLevel.Verbose) + .CreateLogger(); + + services.AddSingleton(new LoggerFactory().AddSerilog(serilogLogger, dispose: true)); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + + return services; + } + } + } + ``` - The `bunit.xunit` package has been removed, since it is no longer needed (there is no code left in it). @@ -557,16 +560,16 @@ List of new features. - Added `Task ITestRenderer.UnhandledException` property that returns a `Task` that completes when the renderer captures an unhandled exception from a component under test. If a component is missing exception handling of asynchronous operations, e.g. in the `OnInitializedAsync` method, the exception will not break the test, because it happens on another thread. To have a test fail in this scenario, you can await the `UnhandledException` property on the `TestContext.Renderer` property, e.g.: - ```csharp - using var ctx = new TestContext(); + ```csharp + using var ctx = new TestContext(); - var cut = ctx.RenderComponent(); + var cut = ctx.RenderComponent(); - Task waitTimeout = Task.Delay(500).ContinueWith(_ => Task.FromResult(null)).Unwrap(); - Exception? unhandledException = await Task.WhenAny(Renderer.UnhandledException, waitTimeout).Unwrap(); + Task waitTimeout = Task.Delay(500).ContinueWith(_ => Task.FromResult(null)).Unwrap(); + Exception? unhandledException = await Task.WhenAny(Renderer.UnhandledException, waitTimeout).Unwrap(); - Assert.Null(unhandledException); - ``` + Assert.Null(unhandledException); + ``` In this example, we await any unhandled exceptions from the renderer, or our wait timeout. The `waitTimeout` ensures that we will not wait forever, in case no unhandled exception is thrown. @@ -576,42 +579,42 @@ List of new features. - Added a simple fake navigation manager, which is registered by default in bUnit's service provider. When the fake navigation manager's `NavigateTo` method is called, it does two things: - 1. Set the `Uri` property to the URI passed to the `NavigateTo` method (with the URI normalized to an absolute URI). - 2. Raise the `LocationChanged` event with the URI passed to the `NavigateTo` method. + 1. Set the `Uri` property to the URI passed to the `NavigateTo` method (with the URI normalized to an absolute URI). + 2. Raise the `LocationChanged` event with the URI passed to the `NavigateTo` method. - Lets look at an example: To verify that the `` component below calls the `NavigationManager.NavigateTo` method with the expected value, do the following: + Lets look at an example: To verify that the `` component below calls the `NavigationManager.NavigateTo` method with the expected value, do the following: - `` component: + `` component: - ```cshtml - @inject NavigationManager NavMan - @code { - protected override void OnInitialized() - { - NavMan.NavigateTo("foo"); - } - } - ``` + ```cshtml + @inject NavigationManager NavMan + @code { + protected override void OnInitialized() + { + NavMan.NavigateTo("foo"); + } + } + ``` - Test code: + Test code: - ```csharp - // Arrange - using var ctx = new TestContext(); - var navMan = ctx.Services.GetRequiredService(); + ```csharp + // Arrange + using var ctx = new TestContext(); + var navMan = ctx.Services.GetRequiredService(); - // Act - var cut = ctx.RenderComponent(); + // Act + var cut = ctx.RenderComponent(); - // Assert - Assert.Equal($"{navMan.BaseUri}foo", navMan.Uri); - ``` + // Assert + Assert.Equal($"{navMan.BaseUri}foo", navMan.Uri); + ``` - Since the `foo` input argument is normalized to an absolute URI, we have to do the same normalization in our assertion. + Since the `foo` input argument is normalized to an absolute URI, we have to do the same normalization in our assertion. - The fake navigation manager's `BaseUri` is set to `http://localhost/`, but it is not recommended to use that URL directly in your code. Instead create an assertion by getting that value from the `BaseUri` property, like shown in the example above. + The fake navigation manager's `BaseUri` is set to `http://localhost/`, but it is not recommended to use that URL directly in your code. Instead create an assertion by getting that value from the `BaseUri` property, like shown in the example above. - By [@egil](https://github.com/egil) in [#345](https://github.com/egil/bUnit/pull/345). + By [@egil](https://github.com/egil) in [#345](https://github.com/egil/bUnit/pull/345). - Added additional bUnit JSInterop `Setup` methods, that makes it possible to get complete control of invocation matching for the created handler. By [@egil](https://github.com/egil). @@ -647,15 +650,15 @@ List of new features. - Added support for components that call `ElementReference.FocusAsync`. These calls are handled by the bUnits JSInterop, that also allows you to verify that `FocusAsync` has been called for a specific element. For example, if a component has rendered an `` element, then the following code will verify that it has been focused using `FocusAsync`: - ```csharp - var cut = RenderComponent(); + ```csharp + var cut = RenderComponent(); - var input = cut.Find("input"); + var input = cut.Find("input"); - JSInterop.VerifyFocusAsyncInvoke() - .Arguments[0] // the first argument is the ElemenetReference - .ShouldBeElementReferenceTo(input); - ``` + JSInterop.VerifyFocusAsyncInvoke() + .Arguments[0] // the first argument is the ElemenetReference + .ShouldBeElementReferenceTo(input); + ``` By [@egil](https://github.com/egil) in [#260](https://github.com/egil/bUnit/pull/260). @@ -669,21 +672,21 @@ List of new features. For example, to tests the `` page/component that is part of new Blazor apps, do the following (inside a `CounterTest.razor` file): - ```cshtml - @code + ```cshtml + @code + { + [Fact] + public void Counter_Increments_When_Button_Is_Clicked() { - [Fact] - public void Counter_Increments_When_Button_Is_Clicked() - { - using var ctx = new TestContext(); - var cut = ctx.Render(@); + using var ctx = new TestContext(); + var cut = ctx.Render(@); - cut.Find("button").Click(); + cut.Find("button").Click(); - cut.Find("p").MarkupMatches(@

Current count: 1

); - } + cut.Find("p").MarkupMatches(@

Current count: 1

); } - ``` + } + ``` Note: This example uses xUnit, but NUnit or MSTest works equally well. @@ -691,21 +694,21 @@ List of new features. This means the test component above ends up looking like this: - ```cshtml - @inherts TestContext - @code + ```cshtml + @inherts TestContext + @code + { + [Fact] + public void Counter_Increments_When_Button_Is_Clicked() { - [Fact] - public void Counter_Increments_When_Button_Is_Clicked() - { - var cut = Render(@); + var cut = Render(@); - cut.Find("button").Click(); + cut.Find("button").Click(); - cut.Find("p").MarkupMatches(@

Current count: 1

); - } + cut.Find("p").MarkupMatches(@

Current count: 1

); } - ``` + } + ``` Tip: If you have multiple test components in the same folder, you can add a `_Imports.razor` file inside it and add the `@inherits TestContext` statement in that, removing the need to add it to every test component. @@ -713,16 +716,16 @@ List of new features. - Added support for `IJSRuntime.InvokeAsync(...)` calls from components. There is now a new setup helper methods for configuring how invocations towards JS modules should be handled. This is done with the various `SetupModule` methods available on the `BunitJSInterop` type available through the `TestContext.JSInterop` property. For example, to set up a module for handling calls to `foo.js`, do the following: - ```c# - using var ctx = new TestContext(); - var moduleJsInterop = ctx.JSInterop.SetupModule("foo.js"); - ``` + ```c# + using var ctx = new TestContext(); + var moduleJsInterop = ctx.JSInterop.SetupModule("foo.js"); + ``` The returned `moduleJsInterop` is a `BunitJSInterop` type, which means all the normal `Setup` and `SetupVoid` methods can be used to configure it to handle calls to the module from a component. For example, to configure a handler for a call to `hello` in the `foo.js` module, do the following: - ```c# - moduleJsInterop.SetupVoid("hello"); - ``` + ```c# + moduleJsInterop.SetupVoid("hello"); + ``` By [@egil](https://github.com/egil) in [#288](https://github.com/egil/bUnit/pull/288). @@ -785,15 +788,15 @@ List of new features. For example, this makes it easier to trigger keyboard events on an element: - ```csharp - var cut = ctx.RenderComponent(); - var element = cut.Find("input"); + ```csharp + var cut = ctx.RenderComponent(); + var element = cut.Find("input"); - element.KeyDown(Key.Enter + Key.Control); // Triggers onkeydown event with Ctrl + Enter - element.KeyUp(Key.Control + Key.Shift + 'B'); // Triggers onkeyup event with Ctrl + Shift + B - element.KeyPress('1'); // Triggers onkeypress event with key 1 - element.KeyDown(Key.Alt + "<"); // Triggers onkeydown event with Alt + < - ``` + element.KeyDown(Key.Enter + Key.Control); // Triggers onkeydown event with Ctrl + Enter + element.KeyUp(Key.Control + Key.Shift + 'B'); // Triggers onkeyup event with Ctrl + Shift + B + element.KeyPress('1'); // Triggers onkeypress event with key 1 + element.KeyDown(Key.Alt + "<"); // Triggers onkeydown event with Alt + < + ``` By [@duracellko](https://github.com/duracellko) in [#101](https://github.com/egil/bUnit/issues/101). @@ -801,10 +804,10 @@ List of new features. For example, to pass a cascading string value `foo` to all components rendered with the test context, do the following: - ```csharp - ctx.RenderTree>(parameters => parameters.Add(p => p.Value, "foo")); - var cut = ctx.RenderComponent(); - ``` + ```csharp + ctx.RenderTree>(parameters => parameters.Add(p => p.Value, "foo")); + var cut = ctx.RenderComponent(); + ``` By [@egil](https://github.com/egil) in [#236](https://github.com/egil/bUnit/pull/236). @@ -824,16 +827,16 @@ List of changes in existing functionality. For example, this makes it easier to pass e.g. both a markup string and a component to a `ChildContent` parameter: - ```csharp - var cut = ctx.RenderComponent(parameters => parameters - .AddChildContent("

Below you will find a most interesting alert!

") - .AddChildContent(childParams => childParams - .Add(p => p.Heading, "Alert heading") - .Add(p => p.Type, AlertType.Warning) - .AddChildContent("

Hello World

") - ) - ); - ``` + ```csharp + var cut = ctx.RenderComponent(parameters => parameters + .AddChildContent("

Below you will find a most interesting alert!

") + .AddChildContent(childParams => childParams + .Add(p => p.Heading, "Alert heading") + .Add(p => p.Type, AlertType.Warning) + .AddChildContent("

Hello World

") + ) + ); + ``` By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203). @@ -929,9 +932,9 @@ List of changes in existing functionality. There are three big changes in bUnit in this release, as well as a whole host of small new features, improvements to the API, and bug fixes. The three big changes are: -1. A splitting of the library -2. Discovery of razor base tests, and -3. A strongly typed way to pass parameters to a component under test. +1. A splitting of the library +2. Discovery of razor base tests, and +3. A strongly typed way to pass parameters to a component under test. There are also some breaking changes, which we will cover first. @@ -1146,39 +1149,39 @@ The latest version of the library is availble on NuGet: The following example tests the `DelayedRenderOnClick.razor` component: - ```cshtml - // DelayedRenderOnClick.razor -

Times Clicked: @TimesClicked

- - @code - { - public int TimesClicked { get; private set; } + ```cshtml + // DelayedRenderOnClick.razor +

Times Clicked: @TimesClicked

+ + @code + { + public int TimesClicked { get; private set; } - async Task ClickCounter() - { - await Task.Delay(1); // wait 1 millisecond - TimesClicked += 1; - } - } - ``` + async Task ClickCounter() + { + await Task.Delay(1); // wait 1 millisecond + TimesClicked += 1; + } + } + ``` This is a test that uses `WaitForState` to wait until the component under test has a desired state, before the test continues: - ```csharp - [Fact] - public void WaitForStateExample() - { - // Arrange - var cut = RenderComponent(); + ```csharp + [Fact] + public void WaitForStateExample() + { + // Arrange + var cut = RenderComponent(); - // Act - cut.Find("button").Click(); - cut.WaitForState(() => cut.Instance.TimesClicked == 1); + // Act + cut.Find("button").Click(); + cut.WaitForState(() => cut.Instance.TimesClicked == 1); - // Assert - cut.Find("p").TextContent.ShouldBe("Times Clicked: 1"); - } - ``` + // Assert + cut.Find("p").TextContent.ShouldBe("Times Clicked: 1"); + } + ``` - **`WaitForAssertion(Action assertion, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.** This method will wait (block) until the provided assertion method passes, i.e. runs without throwing an assert exception, or until the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the assertion is attempted. @@ -1187,106 +1190,106 @@ The latest version of the library is availble on NuGet: This is a test that tests the `DelayedRenderOnClick.razor` listed above, and that uses `WaitForAssertion` to attempt the assertion each time the component under test renders: - ```csharp - [Fact] - public void WaitForAssertionExample() - { - // Arrange - var cut = RenderComponent(); + ```csharp + [Fact] + public void WaitForAssertionExample() + { + // Arrange + var cut = RenderComponent(); - // Act - cut.Find("button").Click(); + // Act + cut.Find("button").Click(); - // Assert - cut.WaitForAssertion( - () => cut.Find("p").TextContent.ShouldBe("Times Clicked: 1") - ); - } - ``` + // Assert + cut.WaitForAssertion( + () => cut.Find("p").TextContent.ShouldBe("Times Clicked: 1") + ); + } + ``` - **Added support for capturing log statements from the renderer and components under test into the test output.** To enable this, add a constructor to your test classes that takes the `ITestOutputHelper` as input, then in the constructor call `Services.AddXunitLogger` and pass the `ITestOutputHelper` to it, e.g.: - ```csharp - // ComponentTest.cs - public class ComponentTest : ComponentTestFixture - { - public ComponentTest(ITestOutputHelper output) - { - Services.AddXunitLogger(output, minimumLogLevel: LogLevel.Debug); - } + ```csharp + // ComponentTest.cs + public class ComponentTest : ComponentTestFixture + { + public ComponentTest(ITestOutputHelper output) + { + Services.AddXunitLogger(output, minimumLogLevel: LogLevel.Debug); + } - [Fact] - public void Test1() ... - } - ``` + [Fact] + public void Test1() ... + } + ``` For Razor and Snapshot tests, the logger can be added almost the same way. The big difference is that it must be added during _Setup_, e.g.: - ```cshtml - // RazorComponentTest.razor - - ... - - @code { - private ITestOutputHelper _output; + ```cshtml + // RazorComponentTest.razor + + ... + + @code { + private ITestOutputHelper _output; - public RazorComponentTest(ITestOutputHelper output) - { - _output = output; - } + public RazorComponentTest(ITestOutputHelper output) + { + _output = output; + } - void Setup() - { - Services.AddXunitLogger(_output, minimumLogLevel: LogLevel.Debug); - } - } - ``` + void Setup() + { + Services.AddXunitLogger(_output, minimumLogLevel: LogLevel.Debug); + } + } + ``` - **Added simpler `Template` helper method** To make it easier to test components with `RenderFragment` parameters (template components) in C# based tests, a new `Template(string name, Func markupFactory)` helper methods have been added. It allows you to create a mock template that uses the `markupFactory` to create the rendered markup from the template. This is an example of testing the `SimpleWithTemplate.razor`, which looks like this: - ```cshtml - @typeparam T - @foreach (var d in Data) - { - @Template(d); - } - @code - { - [Parameter] public RenderFragment Template { get; set; } - [Parameter] public IReadOnlyList Data { get; set; } = Array.Empty(); - } - ``` + ```cshtml + @typeparam T + @foreach (var d in Data) + { + @Template(d); + } + @code + { + [Parameter] public RenderFragment Template { get; set; } + [Parameter] public IReadOnlyList Data { get; set; } = Array.Empty(); + } + ``` And the test code: - ```csharp - var cut = RenderComponent>( - ("Data", new int[] { 1, 2 }), - Template("Template", num => $"

{num}

") - ); + ```csharp + var cut = RenderComponent>( + ("Data", new int[] { 1, 2 }), + Template("Template", num => $"

{num}

") + ); - cut.MarkupMatches("

1

2

"); - ``` + cut.MarkupMatches("

1

2

"); + ``` Using the more general `Template` helper methods, you need to write the `RenderTreeBuilder` logic yourself, e.g.: - ```csharp - var cut = RenderComponent>( - ("Data", new int[] { 1, 2 }), - Template("Template", num => builder => builder.AddMarkupContent(0, $"

{num}

")) - ); - ``` + ```csharp + var cut = RenderComponent>( + ("Data", new int[] { 1, 2 }), + Template("Template", num => builder => builder.AddMarkupContent(0, $"

{num}

")) + ); + ``` - **Added logging to TestRenderer.** To make it easier to understand the rendering life-cycle during a test, the `TestRenderer` will now log when ever it dispatches an event or renders a component (the log statements can be access by capturing debug logs in the test results, as mentioned above). - **Added some of the Blazor frameworks end-2-end tests.** To get better test coverage of the many rendering scenarios supported by Blazor, the [ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) tests from the Blazor frameworks test suite has been converted from a Selenium to a bUnit. The testing style is very similar, so few changes was necessary to port the tests. The two test classes are here, if you want to compare: - - [bUnit's ComponentRenderingTest.cs](/main/tests/BlazorE2E/ComponentRenderingTest.cs) - - [Blazor's ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) + - [bUnit's ComponentRenderingTest.cs](/main/tests/BlazorE2E/ComponentRenderingTest.cs) + - [Blazor's ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) ### Changed @@ -1305,49 +1308,49 @@ The latest version of the library is availble on NuGet: Here are two example tests, that both test the following `ClickAddsLi.razor` component: - ```cshtml -
    - @foreach (var x in Enumerable.Range(0, Counter)) - { -
  • @x
  • - } -
- - @code { - public int Counter { get; set; } = 0; - } - ``` + ```cshtml +
    + @foreach (var x in Enumerable.Range(0, Counter)) + { +
  • @x
  • + } +
+ + @code { + public int Counter { get; set; } = 0; + } + ``` The first tests uses auto refresh, set through the optional parameter `enableAutoRefresh` passed to FindAll: - ```csharp - public void AutoRefreshQueriesForNewElementsAutomatically() - { - var cut = RenderComponent(); - var liElements = cut.FindAll("li", enableAutoRefresh: true); - liElements.Count.ShouldBe(0); + ```csharp + public void AutoRefreshQueriesForNewElementsAutomatically() + { + var cut = RenderComponent(); + var liElements = cut.FindAll("li", enableAutoRefresh: true); + liElements.Count.ShouldBe(0); - cut.Find("button").Click(); + cut.Find("button").Click(); - liElements.Count.ShouldBe(1); - } - ``` + liElements.Count.ShouldBe(1); + } + ``` The second test refreshes the collection manually through the `Refresh()` method on the collection: - ```csharp - public void RefreshQueriesForNewElements() - { - var cut = RenderComponent(); - var liElements = cut.FindAll("li"); - liElements.Count.ShouldBe(0); + ```csharp + public void RefreshQueriesForNewElements() + { + var cut = RenderComponent(); + var liElements = cut.FindAll("li"); + liElements.Count.ShouldBe(0); - cut.Find("button").Click(); + cut.Find("button").Click(); - liElements.Refresh(); // Refresh the collection - liElements.Count.ShouldBe(1); - } - ``` + liElements.Refresh(); // Refresh the collection + liElements.Count.ShouldBe(1); + } + ``` - **Custom exception when event handler is missing.** Attempting to triggering a event handler on an element which does not have an handler attached now throws a `MissingEventHandlerException` exception, instead of an `ArgumentException`. @@ -1367,58 +1370,32 @@ The latest version of the library is availble on NuGet: - **Wrong casing on keyboard event dispatch helpers.** The helper methods for the keyboard events was not probably cased, so that has been updated. E.g. from `Keypress(...)` to `KeyPress(...)`. -[Unreleased]: https://github.com/bUnit-dev/bUnit/compare/v1.27.17...HEAD - +[unreleased]: https://github.com/bUnit-dev/bUnit/compare/v1.28.9...HEAD +[1.28.9]: https://github.com/bUnit-dev/bUnit/compare/v1.27.17...v1.28.9 [1.27.17]: https://github.com/bUnit-dev/bUnit/compare/v1.26.64...1.27.17 - [1.26.64]: https://github.com/bUnit-dev/bUnit/compare/v1.25.3...v1.26.64 - [1.25.3]: https://github.com/bUnit-dev/bUnit/compare/v1.24.10...1.25.3 - [1.24.10]: https://github.com/bUnit-dev/bUnit/compare/v1.23.9...v1.24.10 - [1.23.9]: https://github.com/bUnit-dev/bUnit/compare/v1.22.19...1.23.9 - [1.22.19]: https://github.com/bUnit-dev/bUnit/compare/v1.21.9...v1.22.19 - [1.21.9]: https://github.com/bUnit-dev/bUnit/compare/v1.20.8...1.21.9 - [1.20.8]: https://github.com/bUnit-dev/bUnit/compare/v1.19.14...v1.20.8 - [1.19.14]: https://github.com/bUnit-dev/bUnit/compare/v1.18.4...1.19.14 - [1.18.4]: https://github.com/bUnit-dev/bUnit/compare/v1.17.2...v1.18.4 - [1.17.2]: https://github.com/bUnit-dev/bUnit/compare/v1.16.2...1.17.2 - [1.16.2]: https://github.com/bUnit-dev/bUnit/compare/v1.15.5...v1.16.2 - [1.15.5]: https://github.com/bUnit-dev/bUnit/compare/v1.14.4...1.15.5 - [1.14.4]: https://github.com/bUnit-dev/bUnit/compare/v1.13.5...v1.14.4 - [1.13.5]: https://github.com/bUnit-dev/bUnit/compare/v1.12.6...1.13.5 - [1.12.6]: https://github.com/bUnit-dev/bUnit/compare/v1.11.7...v1.12.6 - [1.11.7]: https://github.com/bUnit-dev/bUnit/compare/v1.10.14...v1.11.7 - [1.10.14]: https://github.com/bUnit-dev/bUnit/compare/v1.9.8...v1.10.14 - [1.9.8]: https://github.com/bUnit-dev/bUnit/compare/v1.8.15...v1.9.8 - [1.8.15]: https://github.com/bUnit-dev/bUnit/compare/v1.7.7...v1.8.15 - [1.7.7]: https://github.com/bUnit-dev/bUnit/compare/v1.6.4...v1.7.7 - [1.6.4]: https://github.com/bUnit-dev/bUnit/compare/v1.5.12...v1.6.4 - [1.5.12]: https://github.com/bUnit-dev/bUnit/compare/v1.4.15...v1.5.12 - [1.4.15]: https://github.com/bUnit-dev/bUnit/compare/v1.3.42...v1.4.15 - [1.3.42]: https://github.com/bUnit-dev/bUnit/compare/v1.2.49...v1.3.42 - [1.2.49]: https://github.com/bUnit-dev/bUnit/compare/v1.1.5...v1.2.49 - [1.1.5]: https://github.com/bUnit-dev/bUnit/compare/v1.0.16...v1.1.5