diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf6a90b80..a09b7a5b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,12 @@ All notable changes to **bUnit** will be documented in this file. The project ad
## [Unreleased]
+### Added
+- Added `FindByAllByLabel` to `bunit.web.query` package. By [@linkdotnet](https://github.com/linkdotnet).
+
+### Fixed
+- Updated `AngleSharp.Diffing` to fix a bug related to unknown HTML elements. Reported by [@md-at-slashwhy](https://github.com/md-at-slashwhy).
+
## [2.1.1] - 2025-11-21
### Changed
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 73e40e4e4..d99488398 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,9 +6,9 @@
-
+
-
+
@@ -17,8 +17,8 @@
-
-
+
+
@@ -81,18 +81,18 @@
-
+
-
+
-
-
-
+
+
+
diff --git a/bunit.sln b/bunit.sln
deleted file mode 100644
index e5b5294d4..000000000
--- a/bunit.sln
+++ /dev/null
@@ -1,123 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31710.8
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".items", ".items", "{A5D7B605-02D8-468C-9BDF-864CF93B12F9}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- .gitattributes = .gitattributes
- .gitignore = .gitignore
- Directory.Build.props = Directory.Build.props
- Directory.Packages.props = Directory.Packages.props
- .config\dotnet-tools.json = .config\dotnet-tools.json
- global.json = global.json
- version.json = version.json
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9A2B3B34-D41C-43E8-BC7D-246BEBE48D59}"
- ProjectSection(SolutionItems) = preProject
- src\.editorconfig = src\.editorconfig
- src\Directory.Build.props = src\Directory.Build.props
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{6EA09ED4-B714-4E6F-B0E1-4D987F8AE520}"
- ProjectSection(SolutionItems) = preProject
- tests\.editorconfig = tests\.editorconfig
- tests\Directory.Build.props = tests\Directory.Build.props
- tests\run-tests.ps1 = tests\run-tests.ps1
- tests\xunit.runner.json = tests\xunit.runner.json
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".text", ".text", "{392FCD4E-356A-412A-A854-8EE197EA65B9}"
- ProjectSection(SolutionItems) = preProject
- CHANGELOG.md = CHANGELOG.md
- README.md = README.md
- MIGRATION.md = MIGRATION.md
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit", "src\bunit\bunit.csproj", "{1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.template", "src\bunit.template\bunit.template.csproj", "{6127D121-9387-451B-B15D-8350A32D3001}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.testassets", "tests\bunit.testassets\bunit.testassets.csproj", "{7972A80F-30DC-4EF4-9294-7D4DD2965882}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".workflows", ".workflows", "{3B2F3419-5336-4147-A212-E19091195203}"
- ProjectSection(SolutionItems) = preProject
- .github\workflows\ci.yml = .github\workflows\ci.yml
- .github\workflows\docs-deploy.yml = .github\workflows\docs-deploy.yml
- .github\workflows\prepare-release.yml = .github\workflows\prepare-release.yml
- .github\workflows\release.yml = .github\workflows\release.yml
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.web.query", "src\bunit.web.query\bunit.web.query.csproj", "{0FF92169-7D8F-46A2-8327-A2F028CB426F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.web.query.tests", "tests\bunit.web.query.tests\bunit.web.query.tests.csproj", "{DE975A0C-0672-4248-913E-D267C1001801}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.generators.internal", "src\bunit.generators.internal\bunit.generators.internal.csproj", "{AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.generators", "src\bunit.generators\bunit.generators.csproj", "{A7C6A2AA-FF8F-4ED1-8590-5324FC566059}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bunit.tests", "tests\bunit.tests\bunit.tests.csproj", "{56889DE7-5E66-4E9C-815B-CBCFC9961612}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bunit.generators.tests", "tests\bunit.generators.tests\bunit.generators.tests.csproj", "{D08F7F1D-74B1-4A76-86A2-94918863740C}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|Any CPU.Build.0 = Release|Any CPU
- {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6127D121-9387-451B-B15D-8350A32D3001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6127D121-9387-451B-B15D-8350A32D3001}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|Any CPU.Build.0 = Release|Any CPU
- {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|Any CPU.Build.0 = Release|Any CPU
- {DE975A0C-0672-4248-913E-D267C1001801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DE975A0C-0672-4248-913E-D267C1001801}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DE975A0C-0672-4248-913E-D267C1001801}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DE975A0C-0672-4248-913E-D267C1001801}.Release|Any CPU.Build.0 = Release|Any CPU
- {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|Any CPU.Build.0 = Release|Any CPU
- {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|Any CPU.Build.0 = Release|Any CPU
- {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|Any CPU.Build.0 = Release|Any CPU
- {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59}
- {6127D121-9387-451B-B15D-8350A32D3001} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59}
- {7972A80F-30DC-4EF4-9294-7D4DD2965882} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520}
- {0FF92169-7D8F-46A2-8327-A2F028CB426F} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59}
- {DE975A0C-0672-4248-913E-D267C1001801} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520}
- {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59}
- {A7C6A2AA-FF8F-4ED1-8590-5324FC566059} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59}
- {56889DE7-5E66-4E9C-815B-CBCFC9961612} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520}
- {D08F7F1D-74B1-4A76-86A2-94918863740C} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {24106918-1C86-4769-BDA6-9C80E64CD260}
- EndGlobalSection
-EndGlobal
diff --git a/bunit.slnx b/bunit.slnx
new file mode 100644
index 000000000..7294d3ede
--- /dev/null
+++ b/bunit.slnx
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/README.md b/docs/README.md
index 5baf50e7a..0568e6f0d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -28,7 +28,7 @@ The following tools are required to build and view the documentation locally:
### View the documentation
-1. Build the `bunit.sln` solution in the root folder in release configuration `dotnet build -c Release`.
+1. Build the `bunit.slxn` solution in the root folder in release configuration `dotnet build -c Release`.
2. From `docs/site` run `docfx metadata` to generate the documentation site's metadata.
3. After that run `docfx build` to generate the documentation site.
4. From `docs/` run `serve-docs.cmd`. This will start up a local web server (using `dotnet serve`), hosting the generated documentation site.
diff --git a/src/bunit.web.query/Labels/LabelQueryExtensions.cs b/src/bunit.web.query/Labels/LabelQueryExtensions.cs
index d388f7bf7..b4639cb4f 100644
--- a/src/bunit.web.query/Labels/LabelQueryExtensions.cs
+++ b/src/bunit.web.query/Labels/LabelQueryExtensions.cs
@@ -1,5 +1,6 @@
using AngleSharp.Dom;
using Bunit.Labels.Strategies;
+using Bunit.Web.AngleSharp;
namespace Bunit;
@@ -35,6 +36,25 @@ public static IElement FindByLabelText(this IRenderedComponent rende
return FindByLabelTextInternal(renderedComponent, labelText, options) ?? throw new LabelNotFoundException(labelText);
}
+ ///
+ /// Returns all elements (i.e. input, select, textarea, etc. elements) 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 FindAllByLabelText.
+ /// A read-only collection of elements matching the label text. Returns an empty collection if no matches are found.
+ public static IReadOnlyList FindAllByLabelText(this IRenderedComponent renderedComponent, string labelText, Action? configureOptions = null)
+ {
+ var options = ByLabelTextOptions.Default;
+ if (configureOptions is not null)
+ {
+ options = options with { };
+ configureOptions.Invoke(options);
+ }
+
+ return FindAllByLabelTextInternal(renderedComponent, labelText, options);
+ }
+
internal static IElement? FindByLabelTextInternal(this IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
{
foreach (var strategy in LabelTextQueryStrategies)
@@ -47,4 +67,28 @@ public static IElement FindByLabelText(this IRenderedComponent rende
return null;
}
+
+ internal static IReadOnlyList FindAllByLabelTextInternal(this IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
+ {
+ var results = new List();
+
+ foreach (var strategy in LabelTextQueryStrategies)
+ {
+ results.AddRange(strategy.FindElements(renderedComponent, labelText, options));
+ }
+
+ var seen = new HashSet();
+ var distinctResults = new List();
+
+ foreach (var element in results)
+ {
+ var underlyingElement = element.Unwrap();
+ if (seen.Add(underlyingElement))
+ {
+ distinctResults.Add(element);
+ }
+ }
+
+ return distinctResults;
+ }
}
diff --git a/src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs b/src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs
index 0477c79f4..d53385591 100644
--- a/src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs
+++ b/src/bunit.web.query/Labels/Strategies/ILabelTextQueryStrategy.cs
@@ -4,5 +4,8 @@ namespace Bunit.Labels.Strategies;
internal interface ILabelTextQueryStrategy
{
- IElement? FindElement(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options);
+ IElement? FindElement(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
+ => FindElements(renderedComponent, labelText, options).FirstOrDefault();
+
+ IEnumerable FindElements(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options);
}
diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs
index e2c0a4329..771ada084 100644
--- a/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs
+++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelStrategy.cs
@@ -5,7 +5,7 @@ namespace Bunit.Labels.Strategies;
internal sealed class LabelTextUsingAriaLabelStrategy : ILabelTextQueryStrategy
{
- public IElement? FindElement(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
+ public IEnumerable FindElements(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
{
var caseSensitivityQualifier = options.ComparisonType switch
{
@@ -15,11 +15,11 @@ internal sealed class LabelTextUsingAriaLabelStrategy : ILabelTextQueryStrategy
_ => ""
};
- var element = renderedComponent.Nodes.TryQuerySelector($"[aria-label='{labelText}'{caseSensitivityQualifier}]");
+ var elements = renderedComponent.Nodes.TryQuerySelectorAll($"[aria-label='{labelText}'{caseSensitivityQualifier}]");
- if (element is null)
- return null;
-
- return element.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
+ foreach (var element in elements)
+ {
+ yield return element.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
+ }
}
}
diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs
index 36108d892..f8f5ab87c 100644
--- a/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs
+++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingAriaLabelledByStrategy.cs
@@ -5,7 +5,7 @@ namespace Bunit.Labels.Strategies;
internal sealed class LabelTextUsingAriaLabelledByStrategy : ILabelTextQueryStrategy
{
- public IElement? FindElement(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
+ public IEnumerable FindElements(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
{
var elementsWithAriaLabelledBy = renderedComponent.Nodes.TryQuerySelectorAll("[aria-labelledby]");
@@ -13,9 +13,7 @@ internal sealed class LabelTextUsingAriaLabelledByStrategy : ILabelTextQueryStra
{
var labelElement = renderedComponent.Nodes.TryQuerySelector($"#{element.GetAttribute("aria-labelledby")}");
if (labelElement is not null && labelElement.GetInnerText().Equals(labelText, options.ComparisonType))
- return element.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
+ yield return element.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
}
-
- return null;
}
}
diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs
index d9577032a..2ab1931bf 100644
--- a/src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs
+++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingForAttributeStrategy.cs
@@ -5,19 +5,20 @@ namespace Bunit.Labels.Strategies;
internal sealed class LabelTextUsingForAttributeStrategy : ILabelTextQueryStrategy
{
- public IElement? FindElement(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
+ public IEnumerable FindElements(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
{
- var matchingLabel = renderedComponent.Nodes.TryQuerySelectorAll("label")
- .SingleOrDefault(l => l.TextContent.Trim().Equals(labelText, options.ComparisonType));
+ var matchingLabels = renderedComponent.Nodes.TryQuerySelectorAll("label")
+ .Where(l => l.TextContent.Trim().Equals(labelText, options.ComparisonType));
- if (matchingLabel is null)
- return null;
+ foreach (var matchingLabel in matchingLabels)
+ {
+ var forAttribute = matchingLabel.GetAttribute("for");
+ if (string.IsNullOrEmpty(forAttribute))
+ continue;
- var matchingElement = renderedComponent.Nodes.TryQuerySelector($"#{matchingLabel.GetAttribute("for")}");
-
- if (matchingElement is null)
- return null;
-
- return matchingElement.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
+ var matchingElement = renderedComponent.Nodes.TryQuerySelector($"#{forAttribute}");
+ if (matchingElement is not null)
+ yield return matchingElement.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
+ }
}
}
diff --git a/src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs b/src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs
index ca2afb9c8..e503c92cc 100644
--- a/src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs
+++ b/src/bunit.web.query/Labels/Strategies/LabelTextUsingWrappedElementStrategy.cs
@@ -5,15 +5,18 @@ namespace Bunit.Labels.Strategies;
internal sealed class LabelTextUsingWrappedElementStrategy : ILabelTextQueryStrategy
{
- public IElement? FindElement(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
+ public IEnumerable FindElements(IRenderedComponent renderedComponent, string labelText, ByLabelTextOptions options)
{
- var matchingLabel = renderedComponent.Nodes.TryQuerySelectorAll("label")
- .SingleOrDefault(l => l.GetInnerText().Trim().StartsWith(labelText, options.ComparisonType));
+ var matchingLabels = renderedComponent.Nodes.TryQuerySelectorAll("label")
+ .Where(l => l.GetInnerText().Trim().StartsWith(labelText, options.ComparisonType));
- var matchingElement = matchingLabel?
- .Children
- .SingleOrDefault(n => n.IsHtmlElementThatCanHaveALabel());
-
- return matchingElement?.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
+ foreach (var matchingLabel in matchingLabels)
+ {
+ var matchingElements = matchingLabel.Children.Where(n => n.IsHtmlElementThatCanHaveALabel());
+ foreach (var matchingElement in matchingElements)
+ {
+ yield return matchingElement.WrapUsing(new ByLabelTextElementFactory(renderedComponent, labelText, options));
+ }
+ }
}
}
diff --git a/src/bunit/InternalsVisibleTo.cs b/src/bunit/InternalsVisibleTo.cs
index a3c9cf4b0..6bdae613c 100644
--- a/src/bunit/InternalsVisibleTo.cs
+++ b/src/bunit/InternalsVisibleTo.cs
@@ -1 +1,2 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Bunit.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010001be6b1a2ca57b09b7040e2ab0993e515296ae22aef4031a4fe388a1336fe21f69c7e8610e9935de6ed18d94b5c98429f99ef62ce3d0af28a7088f856239368ea808ad4c448aa2a8075ed581f989f36ed0d0b8b1cfcaf1ff6a4506c8a99b7024b6eb56996d08e3c9c1cf5db59bff96fcc63ccad155ef7fc63aab6a69862437b6")]
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Bunit.Web.Query, PublicKey=002400000480000094000000060200000024000052534131000400000100010001be6b1a2ca57b09b7040e2ab0993e515296ae22aef4031a4fe388a1336fe21f69c7e8610e9935de6ed18d94b5c98429f99ef62ce3d0af28a7088f856239368ea808ad4c448aa2a8075ed581f989f36ed0d0b8b1cfcaf1ff6a4506c8a99b7024b6eb56996d08e3c9c1cf5db59bff96fcc63ccad155ef7fc63aab6a69862437b6")]
diff --git a/tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTest.cs b/tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTest.cs
index c58ae88b1..f59da3e7c 100644
--- a/tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTest.cs
+++ b/tests/bunit.web.query.tests/Labels/LabelQueryExtensionsTest.cs
@@ -351,4 +351,146 @@ public void Test021(string htmlElementWithLabel)
input.NodeName.ShouldBe(htmlElementWithLabel, StringCompareShould.IgnoreCase);
input.Id.ShouldBe($"{htmlElementWithLabel}-with-label");
}
+
+ [Fact(DisplayName = "FindAllByLabelText should return empty collection when no elements match")]
+ public void Test100()
+ {
+ var cut = Render(ps =>
+ ps.AddChildContent("No labels here
"));
+
+ var elements = cut.FindAllByLabelText("Non-existent label");
+
+ elements.ShouldBeEmpty();
+ }
+
+ [Fact(DisplayName = "FindAllByLabelText should return multiple elements with same label text using for attribute")]
+ public void Test101()
+ {
+ var labelText = "Same Label";
+ var cut = Render(ps =>
+ ps.AddChildContent($"""
+
+
+
+
+ """));
+
+ var elements = cut.FindAllByLabelText(labelText);
+
+ elements.Count.ShouldBe(2);
+ elements[0].Id.ShouldBe("input-1");
+ elements[1].Id.ShouldBe("input-2");
+ }
+
+ [Fact(DisplayName = "FindAllByLabelText should return multiple elements with same aria-label")]
+ public void Test102()
+ {
+ var labelText = "Aria Label";
+ var cut = Render(ps =>
+ ps.AddChildContent($"""
+
+
+
+ """));
+
+ var elements = cut.FindAllByLabelText(labelText);
+
+ elements.Count.ShouldBe(3);
+ elements[0].Id.ShouldBe("input-1");
+ elements[1].Id.ShouldBe("input-2");
+ elements[2].Id.ShouldBe("button-1");
+ }
+
+ [Fact(DisplayName = "FindAllByLabelText should return multiple elements wrapped in labels")]
+ public void Test103()
+ {
+ var labelText = "Wrapped Label";
+ var cut = Render(ps =>
+ ps.AddChildContent($"""
+
+
+ """));
+
+ var elements = cut.FindAllByLabelText(labelText);
+
+ elements.Count.ShouldBe(2);
+ elements[0].Id.ShouldBe("input-1");
+ elements[1].Id.ShouldBe("input-2");
+ }
+
+ [Fact(DisplayName = "FindAllByLabelText should return multiple elements using aria-labelledby")]
+ public void Test104()
+ {
+ var labelText = "Aria Labelled By";
+ var cut = Render(ps =>
+ ps.AddChildContent($"""
+ {labelText}
+
+
+ """));
+
+ var elements = cut.FindAllByLabelText(labelText);
+
+ elements.Count.ShouldBe(2);
+ elements[0].Id.ShouldBe("input-1");
+ elements[1].Id.ShouldBe("input-2");
+ }
+
+ [Fact(DisplayName = "FindAllByLabelText should return elements from different strategies")]
+ public void Test105()
+ {
+ var labelText = "Mixed Label";
+ var cut = Render(ps =>
+ ps.AddChildContent($"""
+
+
+
+
+ {labelText}
+
+ """));
+
+ var elements = cut.FindAllByLabelText(labelText);
+
+ elements.Count.ShouldBe(4);
+ var ids = elements.Select(e => e.Id).ToList();
+ ids.ShouldContain("input-for");
+ ids.ShouldContain("input-aria");
+ ids.ShouldContain("input-wrapped");
+ ids.ShouldContain("input-labelledby");
+ }
+
+ [Fact(DisplayName = "FindAllByLabelText should deduplicate elements matched by multiple strategies")]
+ public void Test106()
+ {
+ var labelText = "Duplicate Label";
+ var cut = Render(ps =>
+ ps.AddChildContent($"""
+
+
+ """));
+
+ var elements = cut.FindAllByLabelText(labelText);
+
+ // The same input is matched by both 'for' attribute and 'aria-label' strategies
+ // but should only appear once in the result
+ elements.Count.ShouldBe(1);
+ elements[0].Id.ShouldBe("input-1");
+ }
+
+ [Fact(DisplayName = "FindAllByLabelText should respect case-insensitive comparison option")]
+ public void Test107()
+ {
+ var cut = Render(ps =>
+ ps.AddChildContent("""
+
+
+
+
+ """));
+
+ var elements = cut.FindAllByLabelText("label text", o => o.ComparisonType = StringComparison.OrdinalIgnoreCase);
+
+ elements.Count.ShouldBe(2);
+ }
}
diff --git a/tests/run-tests.ps1 b/tests/run-tests.ps1
index 3b8fe0688..b9429b8af 100644
--- a/tests/run-tests.ps1
+++ b/tests/run-tests.ps1
@@ -2,7 +2,7 @@ $maxRuns = $args[0]
$mode = $args[1]
$filter = $args[2]
-dotnet build ..\bunit.sln -c $mode --nologo
+dotnet build ..\bunit.slnx -c $mode --nologo
for ($num = 1 ; $num -le $maxRuns ; $num++)
{
@@ -10,10 +10,10 @@ for ($num = 1 ; $num -le $maxRuns ; $num++)
if($filter)
{
- dotnet test ..\bunit.sln -c $mode --no-restore --no-build --blame --nologo --filter $filter
+ dotnet test ..\bunit.slnx -c $mode --no-restore --no-build --blame --nologo --filter $filter
}
else
{
- dotnet test ..\bunit.sln -c $mode --no-restore --no-build --blame --nologo
+ dotnet test ..\bunit.slnx -c $mode --no-restore --no-build --blame --nologo
}
}
diff --git a/version.json b/version.json
index 1bddb4867..13e4cdccc 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": "2.1",
+ "version": "2.2",
"assemblyVersion": {
"precision": "revision"
},