Skip to content

Commit

Permalink
Fix mechRepr on controls to always return a native str.
Browse files Browse the repository at this point in the history
The error scenario is the following:

* A page contains a submit control whose value contains a non-ASCII character.
* There is also a select with options or a text input field.
* browser.getControl() is used to select a _not_ existing control.
* TestBrowser tries to render the list of possible controls using the mechRepr of the controls.
* The previous step fails with a UnicodeDecodeError when joining the mechRepr values of the controls because the ones of select option (aka item) and text field are unicode while the one of the submit control is str but contains a non-ASCII char.

As the scenario described above requires the mechRepr to be of the same type I decided to adapt the two remaining control classes which returned unicode to the str approach used by of the majority of the controls.
  • Loading branch information
mgedmin authored and Michael Howitz committed Oct 16, 2017
1 parent 86ef512 commit 6e33115
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 10 deletions.
2 changes: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ CHANGES
5.2.3 (unreleased)
------------------

- Nothing changed yet.
- Fix ``mechRepr`` on controls to always return a native str.


5.2.2 (2017-10-10)
Expand Down
14 changes: 8 additions & 6 deletions src/zope/testbrowser/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ def controls(self):

def mechRepr(self):
# emulate mechanize control representation
toStr = self.browser.toStr
ctrl = self._control
if isinstance(ctrl, webtest.forms.Text):
tp = ctrl.attrs.get('type')
Expand All @@ -753,7 +754,7 @@ def mechRepr(self):
}
clname = classnames.get(tp, "TextControl")
return "<%s(%s=%s)%s>" % (
clname, ctrl.name, ctrl.value,
clname, toStr(ctrl.name), toStr(ctrl.value),
' (%s)' % (', '.join(infos)) if infos else '')

if isinstance(ctrl, webtest.forms.File):
Expand Down Expand Up @@ -1120,11 +1121,12 @@ def labels(self):
for lbl in labels if lbl]

def mechRepr(self):
contents = normalizeWhitespace(self._elem.text)
id = self._elem.attrs.get('id')
label = self._elem.attrs.get('label', contents)
value = self._value
name = self._elem.attrs.get('name', value) # XXX wha????
toStr = self.browser.toStr
contents = toStr(normalizeWhitespace(self._elem.text))
id = toStr(self._elem.attrs.get('id'))
label = toStr(self._elem.attrs.get('label', contents))
value = toStr(self._value)
name = toStr(self._elem.attrs.get('name', value)) # XXX wha????
return (
"<Item name='%s' id=%s contents='%s' value='%s' label='%s'>"
) % (name, id, contents, value, label)
Expand Down
54 changes: 51 additions & 3 deletions src/zope/testbrowser/tests/test_browser.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
Expand Down Expand Up @@ -121,6 +122,47 @@ def test_displayValue_set_empty_value(self):
self.assertEqual(self.control.displayValue, [])


class TestMechRepr(unittest.TestCase):
"""Testing ..browser.*.mechRepr()."""

def setUp(self):
super(TestMechRepr, self).setUp()
app = TestApp()
app.set_next_response(b'''\
<html>
<body>
<form>
<input name="inp1" type="text" value="Täkst" />
<select name="sel1">
<option value="op">Türn</option>
</select>
<input name="sub1" type="submit" value="Yës" />
</form>
</body>
</html>''')
self.browser = Browser(wsgi_app=app)
self.browser.open('https://localhost')

def test_TextControl_has_str_mechRepr(self):
mech_repr = self.browser.getControl(name='inp1').mechRepr()
self.assertIsInstance(mech_repr, str)
self.assertEqual(mech_repr, '<TextControl(inp1=T\xc3\xa4kst)>')

def test_ItemControl_has_str_mechRepr(self):
option = self.browser.getControl(name='sel1').getControl(value="op")
mech_repr = option.mechRepr()
self.assertIsInstance(mech_repr, str)
self.assertEqual(
mech_repr,
"<Item name='op' id=None contents='T\xc3\xbcrn' value='op'"
" label='T\xc3\xbcrn'>")

def test_SubmitControl_has_str_mechRepr(self):
mech_repr = self.browser.getControl(name='sub1').mechRepr()
self.assertIsInstance(mech_repr, str)
self.assertEqual(mech_repr, '<SubmitControl(sub1=Y\xc3\xabs)>')


def test_relative_redirect(self):
"""
>>> app = YetAnotherTestApp()
Expand Down Expand Up @@ -1079,9 +1121,15 @@ def test_multiple_classes(self):


def test_suite():
return doctest.DocTestSuite(
checker=zope.testbrowser.tests.helper.checker,
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS)
suite = unittest.TestSuite()
suite.addTests([
unittest.makeSuite(TestDisplayValue),
unittest.makeSuite(TestMechRepr),
doctest.DocTestSuite(
checker=zope.testbrowser.tests.helper.checker,
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS),
])
return suite


# additional_tests is for setuptools "setup.py test" support
Expand Down

0 comments on commit 6e33115

Please sign in to comment.