From 644007cdb74cef584469560ffe12da832abc28a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:25:14 +0000 Subject: [PATCH 1/8] Initial plan From fc32443618a9a0ce24cb161d98c6b6a0eb8fc3f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:35:50 +0000 Subject: [PATCH 2/8] Initial investigation of form submit issue Co-authored-by: egil <105649+egil@users.noreply.github.com> --- docs/samples/components/bunit.docs.samples.csproj | 2 +- src/bunit.web.query/bunit.web.query.csproj | 2 +- src/bunit/bunit.csproj | 2 +- tests/bunit.generators.tests/bunit.generators.tests.csproj | 2 +- tests/bunit.testassets/bunit.testassets.csproj | 2 +- tests/bunit.tests/bunit.tests.csproj | 2 +- tests/bunit.web.query.tests/bunit.web.query.tests.csproj | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/samples/components/bunit.docs.samples.csproj b/docs/samples/components/bunit.docs.samples.csproj index 8d0c766a6..a1265007d 100644 --- a/docs/samples/components/bunit.docs.samples.csproj +++ b/docs/samples/components/bunit.docs.samples.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0 Bunit.Docs.Samples enable CA1014,NU5104 diff --git a/src/bunit.web.query/bunit.web.query.csproj b/src/bunit.web.query/bunit.web.query.csproj index d684833d3..b01356c4e 100644 --- a/src/bunit.web.query/bunit.web.query.csproj +++ b/src/bunit.web.query/bunit.web.query.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0 Bunit Bunit.Web.Query diff --git a/src/bunit/bunit.csproj b/src/bunit/bunit.csproj index 3a5511a05..b693f1212 100644 --- a/src/bunit/bunit.csproj +++ b/src/bunit/bunit.csproj @@ -1,6 +1,6 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0 Bunit diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index a59629e50..b32b5dbbf 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0 Bunit Bunit.Generator.Tests true diff --git a/tests/bunit.testassets/bunit.testassets.csproj b/tests/bunit.testassets/bunit.testassets.csproj index 96fe12aa6..713207766 100644 --- a/tests/bunit.testassets/bunit.testassets.csproj +++ b/tests/bunit.testassets/bunit.testassets.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0 Bunit.TestAssets Bunit.TestAssets Library diff --git a/tests/bunit.tests/bunit.tests.csproj b/tests/bunit.tests/bunit.tests.csproj index 45f6e8727..baefc5c5e 100644 --- a/tests/bunit.tests/bunit.tests.csproj +++ b/tests/bunit.tests/bunit.tests.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0 Bunit Bunit.Tests diff --git a/tests/bunit.web.query.tests/bunit.web.query.tests.csproj b/tests/bunit.web.query.tests/bunit.web.query.tests.csproj index 0c625097d..acf1d553e 100644 --- a/tests/bunit.web.query.tests/bunit.web.query.tests.csproj +++ b/tests/bunit.web.query.tests/bunit.web.query.tests.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0 Bunit Bunit.Web.Query.Tests From ff0de7b8c81e65000fcd6a338f0e2f90f8a30f0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:46:37 +0000 Subject: [PATCH 3/8] Implement support for clicking submit button outside form with form attribute Co-authored-by: egil <105649+egil@users.noreply.github.com> --- .../TriggerEventDispatchExtensions.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs index d5dc40768..eea8b2664 100644 --- a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs +++ b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs @@ -176,10 +176,62 @@ private static bool TryGetParentFormElementSpecialCase( IHtmlButtonElement { Type: "submit", Form: not null } button => button.Form, _ => null }; + + // If form is still null, try to find it by the form attribute for submit buttons/inputs + if (form is null && element.HasAttribute("form")) + { + var isSubmitElement = element switch + { + IHtmlInputElement { Type: "submit" } => true, + IHtmlButtonElement { Type: "submit" } => true, + _ => false + }; + + if (isSubmitElement) + { + var formId = element.GetAttribute("form"); + if (!string.IsNullOrEmpty(formId)) + { + // Try to find the form element by traversing up to find a common ancestor + // and then searching down for the form + form = FindFormById(element, formId); + } + } + } return form is not null && form.TryGetEventId(Htmlizer.ToBlazorAttribute("onsubmit"), out eventId); } + + private static IHtmlFormElement? FindFormById(IElement element, string formId) + { + // First try the owner's GetElementById + var formByOwner = element.Owner?.GetElementById(formId) as IHtmlFormElement; + if (formByOwner is not null) + { + return formByOwner; + } + + // If that didn't work, traverse up to find a common ancestor and search its children + // Start from the parent + var current = element.Parent as IElement; + while (current is not null) + { + // Search children of current element for the form + foreach (var child in current.Children) + { + if ((child.Id == formId || child.GetAttribute("id") == formId) && child is IHtmlFormElement htmlForm) + { + return htmlForm; + } + } + + // Move up to parent + current = current.Parent as IElement; + } + + return null; + } private static bool EventIsDisabled(this IElement element, string eventName) { From 8c440c2b9d8c30e30161c541f3377c94725a85cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:48:54 +0000 Subject: [PATCH 4/8] Improve comments in FindFormById method Co-authored-by: egil <105649+egil@users.noreply.github.com> --- .../TriggerEventDispatchExtensions.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs index eea8b2664..927b2522e 100644 --- a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs +++ b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs @@ -205,19 +205,20 @@ private static bool TryGetParentFormElementSpecialCase( private static IHtmlFormElement? FindFormById(IElement element, string formId) { - // First try the owner's GetElementById + // First try the owner's GetElementById (most efficient if it works) var formByOwner = element.Owner?.GetElementById(formId) as IHtmlFormElement; if (formByOwner is not null) { return formByOwner; } - // If that didn't work, traverse up to find a common ancestor and search its children - // Start from the parent + // If GetElementById didn't work (which can happen with Blazor's incremental DOM rendering), + // traverse up the DOM tree to find a common ancestor and search its children + // This handles cases where the button and form are siblings or in different subtrees var current = element.Parent as IElement; while (current is not null) { - // Search children of current element for the form + // Search children of current element for the form with matching ID foreach (var child in current.Children) { if ((child.Id == formId || child.GetAttribute("id") == formId) && child is IHtmlFormElement htmlForm) @@ -226,7 +227,7 @@ private static bool TryGetParentFormElementSpecialCase( } } - // Move up to parent + // Move up to parent to widen the search current = current.Parent as IElement; } From 150e5dfedfb43a8eac67632d592d4c3851da3796 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:00:55 +0000 Subject: [PATCH 5/8] Restore net10.0 target framework to all projects Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com> --- docs/samples/components/bunit.docs.samples.csproj | 2 +- src/bunit.web.query/bunit.web.query.csproj | 2 +- src/bunit/bunit.csproj | 2 +- tests/bunit.generators.tests/bunit.generators.tests.csproj | 2 +- tests/bunit.testassets/bunit.testassets.csproj | 2 +- tests/bunit.tests/bunit.tests.csproj | 2 +- tests/bunit.web.query.tests/bunit.web.query.tests.csproj | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/samples/components/bunit.docs.samples.csproj b/docs/samples/components/bunit.docs.samples.csproj index a1265007d..8d0c766a6 100644 --- a/docs/samples/components/bunit.docs.samples.csproj +++ b/docs/samples/components/bunit.docs.samples.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 Bunit.Docs.Samples enable CA1014,NU5104 diff --git a/src/bunit.web.query/bunit.web.query.csproj b/src/bunit.web.query/bunit.web.query.csproj index b01356c4e..d684833d3 100644 --- a/src/bunit.web.query/bunit.web.query.csproj +++ b/src/bunit.web.query/bunit.web.query.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 Bunit Bunit.Web.Query diff --git a/src/bunit/bunit.csproj b/src/bunit/bunit.csproj index b693f1212..3a5511a05 100644 --- a/src/bunit/bunit.csproj +++ b/src/bunit/bunit.csproj @@ -1,6 +1,6 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 Bunit diff --git a/tests/bunit.generators.tests/bunit.generators.tests.csproj b/tests/bunit.generators.tests/bunit.generators.tests.csproj index b32b5dbbf..a59629e50 100644 --- a/tests/bunit.generators.tests/bunit.generators.tests.csproj +++ b/tests/bunit.generators.tests/bunit.generators.tests.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 Bunit Bunit.Generator.Tests true diff --git a/tests/bunit.testassets/bunit.testassets.csproj b/tests/bunit.testassets/bunit.testassets.csproj index 713207766..96fe12aa6 100644 --- a/tests/bunit.testassets/bunit.testassets.csproj +++ b/tests/bunit.testassets/bunit.testassets.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 Bunit.TestAssets Bunit.TestAssets Library diff --git a/tests/bunit.tests/bunit.tests.csproj b/tests/bunit.tests/bunit.tests.csproj index baefc5c5e..45f6e8727 100644 --- a/tests/bunit.tests/bunit.tests.csproj +++ b/tests/bunit.tests/bunit.tests.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 Bunit Bunit.Tests diff --git a/tests/bunit.web.query.tests/bunit.web.query.tests.csproj b/tests/bunit.web.query.tests/bunit.web.query.tests.csproj index acf1d553e..0c625097d 100644 --- a/tests/bunit.web.query.tests/bunit.web.query.tests.csproj +++ b/tests/bunit.web.query.tests/bunit.web.query.tests.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 Bunit Bunit.Web.Query.Tests From 32d73732b7159f3c3843dd03784508ec94966486 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:37:41 +0000 Subject: [PATCH 6/8] Fix misleading comment about incremental DOM rendering Co-authored-by: egil <105649+egil@users.noreply.github.com> --- .../TriggerEventDispatchExtensions.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs index 927b2522e..a9acfad5b 100644 --- a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs +++ b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs @@ -212,9 +212,10 @@ private static bool TryGetParentFormElementSpecialCase( return formByOwner; } - // If GetElementById didn't work (which can happen with Blazor's incremental DOM rendering), - // traverse up the DOM tree to find a common ancestor and search its children - // This handles cases where the button and form are siblings or in different subtrees + // If GetElementById didn't work (which can happen when AngleSharp's document ID indexing + // doesn't include all elements), traverse up the DOM tree to find a common ancestor + // and search its children. This handles cases where the button and form are siblings + // or in different subtrees. var current = element.Parent as IElement; while (current is not null) { From 36a5d186d472b0eb01fd194376cab8129b6f584a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:44:07 +0000 Subject: [PATCH 7/8] Add test cases for input element with form attribute outside form Co-authored-by: egil <105649+egil@users.noreply.github.com> --- .../FormWithSubmitElementsOutside.razor | 13 ++++++++++++ .../FormDispatchExtensionTest.cs | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/bunit.testassets/SampleComponents/FormWithSubmitElementsOutside.razor diff --git a/tests/bunit.testassets/SampleComponents/FormWithSubmitElementsOutside.razor b/tests/bunit.testassets/SampleComponents/FormWithSubmitElementsOutside.razor new file mode 100644 index 000000000..869125474 --- /dev/null +++ b/tests/bunit.testassets/SampleComponents/FormWithSubmitElementsOutside.razor @@ -0,0 +1,13 @@ +
+
+ + + +@code { + public bool SubmitWasCalled { get; private set; } + + private void Callback() + { + SubmitWasCalled = true; + } +} diff --git a/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs b/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs index 226affdbd..7ae88ee38 100644 --- a/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs +++ b/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs @@ -41,4 +41,24 @@ public void ClickingOnSubmitButtonOutsideTriggersOnsubmitOfForm() cut.Instance.SubmitWasCalled.ShouldBeTrue(); } + + [Fact] + public void ClickingOnSubmitButtonOutsideWithFormAttributeTriggersOnsubmit() + { + var cut = Render(); + + cut.Find("#submit-button").Click(); + + cut.Instance.SubmitWasCalled.ShouldBeTrue(); + } + + [Fact] + public void ClickingOnSubmitInputOutsideWithFormAttributeTriggersOnsubmit() + { + var cut = Render(); + + cut.Find("#submit-input").Click(); + + cut.Instance.SubmitWasCalled.ShouldBeTrue(); + } } From 29cbd3d9ad5925d05ab6c555ea244fbdc9532100 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:51:32 +0000 Subject: [PATCH 8/8] Simplify FindFormById by removing GetElementById attempt Co-authored-by: egil <105649+egil@users.noreply.github.com> --- .../TriggerEventDispatchExtensions.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs index a9acfad5b..637e8b50a 100644 --- a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs +++ b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs @@ -205,17 +205,9 @@ private static bool TryGetParentFormElementSpecialCase( private static IHtmlFormElement? FindFormById(IElement element, string formId) { - // First try the owner's GetElementById (most efficient if it works) - var formByOwner = element.Owner?.GetElementById(formId) as IHtmlFormElement; - if (formByOwner is not null) - { - return formByOwner; - } - - // If GetElementById didn't work (which can happen when AngleSharp's document ID indexing - // doesn't include all elements), traverse up the DOM tree to find a common ancestor - // and search its children. This handles cases where the button and form are siblings - // or in different subtrees. + // Traverse up the DOM tree to find a common ancestor and search its children + // for the form with the matching ID. This handles cases where the button and + // form are siblings or in different subtrees. var current = element.Parent as IElement; while (current is not null) {