[Checkins] SVN: zope.testbrowser/trunk/ Make getControl() and friends list all matching items when your query fails.
Marius Gedminas
marius at pov.lt
Tue Feb 7 19:41:01 UTC 2012
Log message for revision 124329:
Make getControl() and friends list all matching items when your query fails.
In my experience this ought to make debugging failed testbrowser tests a
lot less painful.
Changed:
U zope.testbrowser/trunk/CHANGES.txt
U zope.testbrowser/trunk/src/zope/testbrowser/README.txt
U zope.testbrowser/trunk/src/zope/testbrowser/browser.py
-=-
Modified: zope.testbrowser/trunk/CHANGES.txt
===================================================================
--- zope.testbrowser/trunk/CHANGES.txt 2012-02-07 19:19:10 UTC (rev 124328)
+++ zope.testbrowser/trunk/CHANGES.txt 2012-02-07 19:41:01 UTC (rev 124329)
@@ -12,10 +12,14 @@
environment by setting it to ``None`` when ``Browser.handleErrors`` is
``True``. This makes it easier to test error pages.
-- More friendly error message from getControl() et al when you specify
- an index that is out of bounds.
+- More friendly error messages from getControl() et al:
+ - when you specify an index that is out of bounds, show the available
+ choices
+ - when you fail to find anything, show all the available items
+
+
4.0.2 (2011-05-25)
------------------
Modified: zope.testbrowser/trunk/src/zope/testbrowser/README.txt
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/README.txt 2012-02-07 19:19:10 UTC (rev 124328)
+++ zope.testbrowser/trunk/src/zope/testbrowser/README.txt 2012-02-07 19:41:01 UTC (rev 124329)
@@ -481,6 +481,11 @@
Traceback (most recent call last):
...
LookupError: label 'Does Not Exist'
+ available items:
+ <TextControl(text-value=Some Text)>
+ <PasswordControl(password-value=Password)>
+ <HiddenControl(hidden-value=Hidden) (readonly)>
+ ...
If you request a control with an ambiguous lookup, the code raises an
AmbiguityError.
@@ -536,6 +541,7 @@
Traceback (most recent call last):
...
LookupError: label 'label needs whitespace normalization'
+ ...
>>> browser.getControl(' Label Needs Whitespace ')
<Control name='label-needs-normalization' type='text'>
>>> browser.getControl('Whitespace')
@@ -544,6 +550,7 @@
Traceback (most recent call last):
...
LookupError: label 'hitespace'
+ ...
>>> browser.getControl('[non word characters should not confuse]')
<Control name='non-word-characters' type='text'>
@@ -579,6 +586,9 @@
Traceback (most recent call last):
...
LookupError: name 'does-not-exist'
+ available items:
+ <TextControl(text-value=Some Text)>
+ ...
>>> browser.getControl(name='ambiguous-control-name', index=1).value
'Second'
@@ -1162,6 +1172,19 @@
</html>
+Pages Without Controls
+~~~~~~~~~~~~~~~~~~~~~~
+
+What would happen if we tried to look up a control on a page that has none?
+
+ >>> browser.open('http://localhost/@@/testbrowser/simple.html')
+ >>> browser.getControl('anything')
+ Traceback (most recent call last):
+ ...
+ LookupError: label 'anything'
+ (there are no form items in the HTML)
+
+
Forms
-----
Modified: zope.testbrowser/trunk/src/zope/testbrowser/browser.py
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/browser.py 2012-02-07 19:19:10 UTC (rev 124328)
+++ zope.testbrowser/trunk/src/zope/testbrowser/browser.py 2012-02-07 19:41:01 UTC (rev 124329)
@@ -33,7 +33,7 @@
_compress_re = re.compile(r"\s+")
compressText = lambda text: _compress_re.sub(' ', text.strip())
-def disambiguate(intermediate, msg, index, choice_repr=None):
+def disambiguate(intermediate, msg, index, choice_repr=None, available=None):
if intermediate:
if index is None:
if len(intermediate) > 1:
@@ -53,6 +53,13 @@
if choice_repr:
msg += ''.join(['\n %d: %s' % (n, choice_repr(choice))
for n, choice in enumerate(intermediate)])
+ else:
+ if available:
+ msg += '\navailable items:' + ''.join([
+ '\n %s' % choice_repr(choice)
+ for choice in available])
+ elif available is not None: # empty list
+ msg += '\n(there are no form items in the HTML)'
raise LookupError(msg)
def control_form_tuple_repr((ctrl, form)):
@@ -348,29 +355,27 @@
"""Select a link and follow it."""
self.getLink(*args, **kw).click()
- def _findByLabel(self, label, forms, include_subcontrols=False):
- # forms are iterable of mech_forms
- matches = re.compile(r'(^|\b|\W)%s(\b|\W|$)'
- % re.escape(compressText(label))).search
- found = []
+ def _findAllControls(self, forms, include_subcontrols=False):
for f in forms:
for control in f.controls:
phantom = control.type in ('radio', 'checkbox')
if not phantom:
- for l in control.get_labels():
- if matches(l.text):
- found.append((control, f))
- break
+ yield (control, f)
if include_subcontrols and (
phantom or control.type=='select'):
-
for i in control.items:
- for l in i.get_labels():
- if matches(l.text):
- found.append((i, f))
- found_one = True
- break
+ yield (i, f)
+ def _findByLabel(self, label, forms, include_subcontrols=False):
+ # forms are iterable of mech_forms
+ matches = re.compile(r'(^|\b|\W)%s(\b|\W|$)'
+ % re.escape(compressText(label))).search
+ found = []
+ for control, form in self._findAllControls(forms, include_subcontrols):
+ for l in control.get_labels():
+ if matches(l.text):
+ found.append((control, form))
+ break
return found
def _findByName(self, name, forms):
@@ -383,22 +388,29 @@
def getControl(self, label=None, name=None, index=None):
"""See zope.testbrowser.interfaces.IBrowser"""
- intermediate, msg = self._get_all_controls(
+ intermediate, msg, available = self._get_all_controls(
label, name, self.mech_browser.forms(), include_subcontrols=True)
control, form = disambiguate(intermediate, msg, index,
- control_form_tuple_repr)
+ control_form_tuple_repr,
+ available)
return controlFactory(control, form, self)
def _get_all_controls(self, label, name, forms, include_subcontrols=False):
onlyOne([label, name], '"label" and "name"')
+ forms = list(forms) # might be an iterator, and we need to iterate twice
+
+ available = None
if label is not None:
res = self._findByLabel(label, forms, include_subcontrols)
msg = 'label %r' % label
elif name is not None:
+ include_subcontrols = False
res = self._findByName(name, forms)
msg = 'name %r' % name
- return res, msg
+ if not res:
+ available = list(self._findAllControls(forms, include_subcontrols))
+ return res, msg, available
def getForm(self, id=None, name=None, action=None, index=None):
zeroOrOne([id, name, action], '"id", "name", and "action"')
@@ -767,13 +779,14 @@
form = self.mech_form
try:
if label is not None or name is not None:
- intermediate, msg = self.browser._get_all_controls(
+ intermediate, msg, available = self.browser._get_all_controls(
label, name, (form,))
intermediate = [
(control, form) for (control, form) in intermediate if
control.type in ('submit', 'submitbutton', 'image')]
control, form = disambiguate(intermediate, msg, index,
- control_form_tuple_repr)
+ control_form_tuple_repr,
+ available)
self.browser._clickSubmit(form, control, coord)
else: # JavaScript sort of submit
if index is not None or coord != (1,1):
@@ -796,8 +809,9 @@
"""See zope.testbrowser.interfaces.IBrowser"""
if self._browser_counter != self.browser._counter:
raise zope.testbrowser.interfaces.ExpiredError
- intermediate, msg = self.browser._get_all_controls(
+ intermediate, msg, available = self.browser._get_all_controls(
label, name, (self.mech_form,), include_subcontrols=True)
control, form = disambiguate(intermediate, msg, index,
- control_form_tuple_repr)
+ control_form_tuple_repr,
+ available)
return controlFactory(control, form, self.browser)
More information about the checkins
mailing list