Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle <option> tag without value option with tests #252

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions mechanicalsoup/browser.py
Expand Up @@ -194,8 +194,10 @@ def _request(self, form, url=None, **kwargs):
data.append((name, tag.text))

elif tag.name == "select":
# If the value attribute is not specified, the content will
# be passed as a value instead.
options = tag.select("option")
selected_values = [i.get("value", "") for i in options
selected_values = [i.get("value", i.text) for i in options
if "selected" in i.attrs]
if "multiple" in tag.attrs:
for value in selected_values:
Expand All @@ -206,7 +208,8 @@ def _request(self, form, url=None, **kwargs):
data.append((name, selected_values[-1]))
elif options:
# Selects the first option if none are selected
data.append((name, options[0].get("value", "")))
first_value = options[0].get("value", options[0].text)
data.append((name, first_value))

if method.lower() == "get":
kwargs["params"] = data
Expand Down
17 changes: 14 additions & 3 deletions mechanicalsoup/form.py
Expand Up @@ -193,9 +193,10 @@ def set_select(self, data):
:param data: Dict of ``{name: value, ...}``.
Find the select element whose *name*-attribute is ``name``.
Then select from among its children the option element whose
*value*-attribute is ``value``. If the select element's
*multiple*-attribute is set, then ``value`` can be a list
or tuple to select multiple options.
*value*-attribute is ``value``. If no matching *value*-attribute
is found, this will search for an option whose text matches
``value``. If the select element's *multiple*-attribute is set,
then ``value`` can be a list or tuple to select multiple options.
"""
for (name, value) in data.items():
select = self.form.find("select", {"name": name})
Expand All @@ -216,6 +217,16 @@ def set_select(self, data):

for choice in value:
option = select.find("option", {"value": choice})

# try to find with text instead of value
if not option:
option = select.find("option", string=choice)

if not option:
raise LinkNotFoundError(
'Option %s not found for select %s' % (choice, name)
)

option.attrs["selected"] = "selected"

def __setitem__(self, name, value):
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
@@ -1,4 +1,4 @@
requests >= 2.0
beautifulsoup4
beautifulsoup4 >= 4.4
six >= 1.4
lxml
35 changes: 35 additions & 0 deletions tests/test_form.py
Expand Up @@ -458,5 +458,40 @@ def test_choose_submit_buttons(expected_post):
assert res.status_code == 200 and res.text == 'Success!'


@pytest.mark.parametrize("fail, selected, expected_post", [
pytest.param(False, 'with_value', [('selector', 'with_value')],
id='Option with value'),
pytest.param(False, 'Without value', [('selector', 'Without value')],
id='Option without value'),
pytest.param(False, 'We have a value here', [('selector', 'with_value')],
id='Option with value selected by its text'),
pytest.param(True, 'Unknown option', None,
id='Unknown option, must raise a LinkNotFound exception')
])
def test_option_without_value(fail, selected, expected_post):
"""Option tag in select can have no value option"""
text = """
<form method="post" action="mock://form.com/post">
<select name="selector">
<option value="with_value">We have a value here</option>
<option>Without value</option>
</select>
<button type="submit">Submit</button>
</form>
"""
browser, url = setup_mock_browser(expected_post=expected_post,
text=text)
browser.open(url)
browser.select_form()
if fail:
with pytest.raises(mechanicalsoup.utils.LinkNotFoundError):
browser['selector'] = selected
else:
browser['selector'] = selected

res = browser.submit_selected()
assert res.status_code == 200 and res.text == 'Success!'


if __name__ == '__main__':
pytest.main(sys.argv)