From ac3f855d4f1d5a56bee85be81fe033a527762dea Mon Sep 17 00:00:00 2001 From: Johannes Jung <81969656+JohaJung@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:59:27 +0200 Subject: [PATCH] bugfix: form input discovery BeatifulSoup considers inputs as equal if they have the same HTML representation. This broke checks like `tag in list_of_tags` in an unexpected way. --- CHANGELOG.rst | 4 +++- tests/html/form_inputs.html | 4 ++++ tests/test_forms.py | 2 ++ webtest/forms.py | 13 +++++++++---- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5650df2..ab594cc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,9 @@ News 3.0.6 (unreleased) ------------------ -- Nothing changed yet. +- Fix a bug that inputs outside of a ``
`` tag were considered + belonging to that form because they had a HTML representation identical + to some input inside that ````. 3.0.5 (2025-06-04) diff --git a/tests/html/form_inputs.html b/tests/html/form_inputs.html index b51abf0..111b1bd 100644 --- a/tests/html/form_inputs.html +++ b/tests/html/form_inputs.html @@ -63,5 +63,9 @@
+ + + diff --git a/tests/test_forms.py b/tests/test_forms.py index aa9da8f..4ce32ef 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -143,6 +143,8 @@ def test_button_submit_by_value_and_index(self): def test_outer_inputs(self): form = self.callFUT(formid='outer_inputs_form') self.assertEqual(('foo', 'bar', 'button'), tuple(form.fields)) + # check that identical input is not considered belonging to this form + self.assertTrue(all(len(itm) == 1 for itm in form.fields.values())) class TestResponseFormAttribute(unittest.TestCase): diff --git a/webtest/forms.py b/webtest/forms.py index dfd9249..1e3114d 100644 --- a/webtest/forms.py +++ b/webtest/forms.py @@ -433,10 +433,15 @@ def _parse_fields(self): field_order = [] tags = ('input', 'select', 'textarea', 'button') inner_elts = self.html.find_all(tags) - def _form_elt_filter(tag): - return tag in inner_elts or ( - tag.attrs.get('form') == self.id and tag.name in tags) - elements = self.response.html.find_all(_form_elt_filter) + if self.id: + def _form_elt_filter(tag): + return tag.name in tags and any( + prt.name == 'form' and prt.attrs.get('id') == self.id + for prt in tag.parents + ) or tag.attrs.get('form') == self.id + elements = self.response.html.find_all(_form_elt_filter) + else: + elements = inner_elts for pos, node in enumerate(elements): attrs = dict(node.attrs) tag = node.name