Skip to content

Commit

Permalink
Merge pull request #89 from cjwatson/strict-displayValue-setter
Browse files Browse the repository at this point in the history
Add error checking to displayValue setters
  • Loading branch information
cjwatson committed Nov 12, 2019
2 parents 8a5c01f + 51bc1fe commit 7d12a97
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
8 changes: 8 additions & 0 deletions CHANGES.rst
Expand Up @@ -9,6 +9,14 @@ CHANGES
``browser.post`` is called directly. See `issue 87
<https://github.com/zopefoundation/zope.testbrowser/issues/87>`_.

- Add error checking to the setters for ``ListControl.displayValue`` and
``CheckboxListControl.displayValue``: in line with the old
``mechanize``-based implementation, these will now raise
``ItemNotFoundError`` if any of the given values are not found, or
``ItemCountError`` on trying to set more than one value on a single-valued
control. See `issue 44
<https://github.com/zopefoundation/zope.testbrowser/issues/44>`_.


5.5.0 (2019-11-11)
------------------
Expand Down
32 changes: 32 additions & 0 deletions docs/narrative.rst
Expand Up @@ -844,6 +844,10 @@ These fields have four other attributes and an additional method:
['Un', 'Deux']
>>> ctrl.value
['1', '2']
>>> ctrl.displayValue = ['Quatre']
Traceback (most recent call last):
...
ItemNotFoundError: Quatre

- 'controls' gives you a list of the subcontrol objects in the control
(subcontrols are discussed below).
Expand Down Expand Up @@ -1074,6 +1078,14 @@ Selection Control (Single-Valued)
['Third']
>>> ctrl.value
['3']
>>> ctrl.displayValue = ['Quatre']
Traceback (most recent call last):
...
ItemNotFoundError: Quatre
>>> ctrl.displayValue = ['Uno', 'Dos']
Traceback (most recent call last):
...
ItemCountError: single selection list, must set sequence of length 0 or 1

Selection Control (Multi-Valued)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1124,6 +1136,10 @@ Checkbox Control (Single-Valued; Unvalued)
>>> browser.getControl(
... name='single-disabled-unvalued-checkbox-value').disabled
True
>>> ctrl.displayValue = ['Nonsense']
Traceback (most recent call last):
...
ItemNotFoundError: Nonsense

Checkbox Control (Single-Valued, Valued)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1166,6 +1182,10 @@ Checkbox Control (Single-Valued, Valued)
False
>>> ctrl.displayValue
[]
>>> ctrl.displayValue = ['Nonsense']
Traceback (most recent call last):
...
ItemNotFoundError: Nonsense

- Checkbox Control (Multi-Valued)

Expand Down Expand Up @@ -1209,6 +1229,10 @@ Checkbox Control (Single-Valued, Valued)
>>> browser.getControl('Three').selected = False
>>> ctrl.value
[]
>>> ctrl.displayValue = ['Four']
Traceback (most recent call last):
...
ItemNotFoundError: Four

Radio Control
~~~~~~~~~~~~~
Expand Down Expand Up @@ -1271,6 +1295,14 @@ This is just unit testing:
['1']
>>> ctrl.displayValue
['Ein']
>>> ctrl.displayValue = ['Vier']
Traceback (most recent call last):
...
ItemNotFoundError: Vier
>>> ctrl.displayValue = ['Ein', 'Zwei']
Traceback (most recent call last):
...
ItemCountError: single selection list, must set sequence of length 0 or 1

The radio control subcontrols were illustrated above.

Expand Down
29 changes: 25 additions & 4 deletions src/zope/testbrowser/browser.py
Expand Up @@ -877,12 +877,19 @@ def displayValue(self, value):

if isinstance(value, string_types):
value = [value]
if not self.multiple and len(value) > 1:
raise ItemCountError(
"single selection list, must set sequence of length 0 or 1")
values = []
found = set()
for key, titles in self._getOptions():
if any(v in t
for t in titles
for v in value):
matches = set(v for t in titles for v in value if v in t)
if matches:
values.append(key)
found.update(matches)
for v in value:
if v not in found:
raise ItemNotFoundError(v)
self.value = values

@property
Expand Down Expand Up @@ -999,8 +1006,14 @@ def displayValue(self):

@displayValue.setter
def displayValue(self, value):
found = set()
for c in self.controls:
c.selected = any(v in c.labels for v in value)
matches = set(v for v in value if v in c.labels)
c.selected = bool(matches)
found.update(matches)
for v in value:
if v not in found:
raise ItemNotFoundError(v)

@property
def multiple(self):
Expand Down Expand Up @@ -1489,3 +1502,11 @@ class BrowserStateError(Exception):

class LinkNotFoundError(IndexError):
pass


class ItemCountError(ValueError):
pass


class ItemNotFoundError(ValueError):
pass
18 changes: 17 additions & 1 deletion src/zope/testbrowser/tests/test_browser.py
Expand Up @@ -20,7 +20,11 @@
import doctest
import unittest

from zope.testbrowser.browser import Browser
from zope.testbrowser.browser import (
Browser,
ItemCountError,
ItemNotFoundError,
)
import zope.testbrowser.tests.helper


Expand Down Expand Up @@ -131,6 +135,18 @@ def test_displayValue_set_empty_value(self):
self.control.displayValue = []
self.assertEqual(self.control.displayValue, [])

def test_displayValue_set_missing_value(self):
self.assertEqual(self.control.displayValue, ['Turn'])
self.assertRaises(
ItemNotFoundError, setattr, self.control, 'displayValue',
['Missing'])

def test_displayValue_set_too_many_values(self):
self.assertEqual(self.control.displayValue, ['Turn'])
self.assertRaises(
ItemCountError, setattr, self.control, 'displayValue',
['Turn', 'Alternative'])


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

0 comments on commit 7d12a97

Please sign in to comment.