Browse files

- This Deform version requires ``colander`` version 0.7.3 or better.

 
- The mapping and sequence item templates now correctly display errors
  with ``msg`` values that are lists.  Previously, a repr of a Python
  list was displayed when a widget had an error with a ``msg`` value
  that was a list; now multiple <p> nodes are inserted into the
  rendering, each <p> node containing an individual error message.
  (Note that this change requires colander 0.7.3).

- Provided better instructions for running the demo app and running
  the tests for the demo app in ``deformdemo/README.txt``.

- Try to prevent false test failures by injecting sleep statements in
  things that use ``browser.key_press``.

- Moved ``deformdemo/tests/test_demo.py`` to ``deformdemo/test.py`` as
  well as moving ``deformdemo/tests/selenium.py`` to
  ``deformdemo/selenium.py``.  Removed the ``deformdemo/tests``
  subdirectory.
  • Loading branch information...
1 parent e169392 commit 85d0b673cc68be32638d30b377263fe9ca2d38e1 @mcdonc mcdonc committed Sep 2, 2010
View
20 CHANGES.txt
@@ -65,7 +65,7 @@ Features
Requirements
~~~~~~~~~~~~
-- This Deform version requires ``colander`` version 0.7.2 or better.
+- This Deform version requires ``colander`` version 0.7.3 or better.
Bug Fixes
~~~~~~~~~
@@ -92,6 +92,13 @@ Bug Fixes
field marker (e.g. ``deformField1``) in the same way it already
changed the value of ``id`` attributes which include a field marker.
+- The mapping and sequence item templates now correctly display errors
+ with ``msg`` values that are lists. Previously, a repr of a Python
+ list was displayed when a widget had an error with a ``msg`` value
+ that was a list; now multiple <p> nodes are inserted into the
+ rendering, each <p> node containing an individual error message.
+ (Note that this change requires colander 0.7.3).
+
Backwards Incompatibilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -108,6 +115,17 @@ Backwards Incompatibilities
Internal
~~~~~~~~
+- Provided better instructions for running the demo app and running
+ the tests for the demo app in ``deformdemo/README.txt``.
+
+- Try to prevent false test failures by injecting sleep statements in
+ things that use ``browser.key_press``.
+
+- Moved ``deformdemo/tests/test_demo.py`` to ``deformdemo/test.py`` as
+ well as moving ``deformdemo/tests/selenium.py`` to
+ ``deformdemo/selenium.py``. Removed the ``deformdemo/tests``
+ subdirectory.
+
- The date input widget now uses JQueryUI's ``datepicker``
functionality rather than relying on JQuery Tools' ``date`` input.
The latter was broken for sequences, and the former works fine.
View
9 deform/templates/mapping_item.pt
@@ -12,8 +12,13 @@
id="req-${field.oid}">*</span>
</label>
<span tal:replace="structure field.serialize(cstruct)"/>
+
<p tal:condition="field.error and not field.widget.hidden"
- id="error-${field.oid}"
- class="${field.widget.error_class}">${field.error.msg}</p>
+ tal:define="errstr 'error-%s' % field.oid"
+ tal:repeat="msg field.error.messages()"
+ tal:attributes="id repeat.msg.index==0 and errstr or
+ ('%s-%s' % (errstr, repeat.msg.index))"
+ class="${field.widget.error_class}">${msg}</p>
+
<!-- /mapping_item -->
</li>
View
9 deform/templates/sequence_item.pt
@@ -10,8 +10,13 @@
alt="Remove" onclick="javascript:$(this).parent().remove();"/>
<span tal:replace="structure field.serialize(cstruct)"/>
+
<p tal:condition="field.error and not field.widget.hidden"
- id="error-${field.oid}"
- class="${field.widget.error_class}">${field.error.msg}</p>
+ tal:define="errstr 'error-%s' % field.oid"
+ tal:repeat="msg field.error.messages()"
+ tal:attributes="id repeat.msg.index==0 and errstr or
+ ('%s-%s' % (errstr, repeat.msg.index))"
+ class="${field.widget.error_class}">${msg}</p>
+
<!-- /sequence_item -->
</li>
View
88 deformdemo/README.txt
@@ -4,26 +4,90 @@ deformdemo
Software which demonstrates the operation of the ``deform`` form
generation package.
-To use it, install deform with the "demo" extra; preferably from
-within a virtualenv::
+Running the Demo
+----------------
- bin/easy_install deform[demo]
+- Create a virtualenv::
-This will cause ``repoze.bfg``, ``Pygments``, ``Babel``, and a number
-of other dependencies to be installed along with deform.
+ $ virtualenv2.6 --no-site-packages /path/to/my/venv
-Get a checkout of deform:
+ Hereafter ``/path/to/my/venv`` will be referred to as $VENV in steps
+ below.
- svn co http://svn.repoze.org/deform/trunk deform
+- Install ``repoze.bfg``, ``pygments`` and ``Babel`` into your
+ virtualenv using ``easy_install``::
-cd to the deform package:
+ $ $VENV/bin/easy_install repoze.bfg pygments Babel
- cd deform
+- Get a checkout of deform::
-To start the demo, run:
+ $ svn co http://svn.repoze.org/deform/trunk deform
- bin/paster serve deformdemo/demo.ini
+- ``cd`` to the newly checked out deform package::
-Visit http://localhost:8521 to see the demo.
+ $ cd deform
+- Run ``setup.py develop`` using the virtualenv's ``python`` command::
+ $ $VENV/bin/python setup.py develop
+
+- While your working directory is still ``deform``, start the demo
+ application::
+
+ $ $VENV/bin/paster serve deformdemo/demo.ini
+
+- Visit http://localhost:8521 in a browser to see the demo.
+
+Running the Demo's Selenium Tests
+---------------------------------
+
+The ``deformdemo`` application serves as a target for functional
+testing during Deform's development. A suite of Selenium tests may be
+run against a local instance of the demonstration application. It is
+wise to run these tests before submitting a patch. Here's how:
+
+- Make sure you have a Java interpreter installed.
+
+- Start the ``deformdemo`` application as described above in "Running
+ the Demo".
+
+- Download `Selenium RC <http://seleniumhq.org/download/>`.
+
+- Unpack the resulting zipfile into a directory (perhaps
+ ``$HOME/opt/selenium``).
+
+- ``cd`` to the ``selenium-server-1.0.3`` subdirectory of the
+ directory to which you unpacked the zipfile.
+
+- Run ``java -jar selenium-server.jar``. Success is defined as seeing
+ output on the console that ends like this::
+
+ 01:49:06.105 INFO - Started SocketListener on 0.0.0.0:4444
+ 01:49:06.105 INFO - Started org.openqa.jetty.jetty.Server@7d2a1e44
+
+- Leave the terminal window in which the selenium server is now
+ running open, and open another terminal window.
+
+- In the new terminal window, cd to the "deform" checkout directory
+ you created above in "Running the Demo"::
+
+ $ cd /path/to/my/deform/checkout
+
+- Run the tests::
+
+ $ $VENV/bin/python deformdemo/test.py
+
+ ``$VENV`` is defined as it was in "Running the Demo" above.
+
+- You will (hopefully) see Firefox pop up in a two-windowed
+ arrangement, and it will begin to display in quick succession the
+ loading of pages in the bottom window and some test output in the
+ top window. The tests will run for a minute or two.
+
+- Test success means that the console window on which you ran
+ ``test.py`` shows a bunch of dots, a test summary, then ``OK``. If
+ it shows a traceback, ``FAILED``, or anything other than a straight
+ line of dots, it means there was an error.
+
+- Fix any errors by modifying your code or by modifying the tests to
+ expect the changes you've made.
View
44 deformdemo/app.py
@@ -911,6 +911,50 @@ def validator(form, value):
form = deform.Form(schema, buttons=('submit',))
return self.render_form(form)
+ @bfg_view(renderer='templates/form.pt', name='multiple_error_messages_map')
+ @demonstrate('Multiple Error Messages For a Single Widget (Mapping)')
+ def multiple_error_messages_mapping(self):
+ def v1(node, value):
+ msg = _('Error ${num}', mapping=dict(num=1))
+ raise colander.Invalid(node, msg)
+ def v2(node, value):
+ msg = _('Error ${num}', mapping=dict(num=2))
+ raise colander.Invalid(node, msg)
+ def v3(node, value):
+ msg = _('Error ${num}', mapping=dict(num=3))
+ raise colander.Invalid(node, msg)
+ class Schema(colander.Schema):
+ field = colander.SchemaNode(
+ colander.String(),
+ title="Fill in a value and submit to see multiple errors",
+ validator = colander.All(v1, v2, v3))
+ schema = Schema()
+ form = deform.Form(schema, buttons=('submit',))
+ return self.render_form(form)
+
+ @bfg_view(renderer='templates/form.pt', name='multiple_error_messages_seq')
+ @demonstrate('Multiple Error Messages For a Single Widget (Sequence)')
+ def multiple_error_messages_seq(self):
+ def v1(node, value):
+ msg = _('Error ${num}', mapping=dict(num=1))
+ raise colander.Invalid(node, msg)
+ def v2(node, value):
+ msg = _('Error ${num}', mapping=dict(num=2))
+ raise colander.Invalid(node, msg)
+ def v3(node, value):
+ msg = _('Error ${num}', mapping=dict(num=3))
+ raise colander.Invalid(node, msg)
+ class Sequence(colander.SequenceSchema):
+ field = colander.SchemaNode(
+ colander.String(),
+ title="Fill in a value and submit to see multiple errors",
+ validator = colander.All(v1, v2, v3))
+ class Schema(colander.Schema):
+ fields = Sequence()
+ schema = Schema()
+ form = deform.Form(schema, buttons=('submit',))
+ return self.render_form(form)
+
@bfg_view(renderer='templates/form.pt', name="multiple_forms")
@demonstrate('Multiple Forms on the Same Page')
def multiple_forms(self):
View
0 deformdemo/tests/selenium.py → deformdemo/selenium.py
File renamed without changes.
View
41 deformdemo/tests/test_demo.py → deformdemo/test.py
@@ -5,7 +5,7 @@
# to run:
# console 1: java -jar selenium-server.jar
# console 2: start the deform demo server (paster serve deformdemo.ini)
-# console 3: python test_demo.py
+# console 3: python test.py
# Instead of using -browserSessionReuse as an arg to
# selenium-server.jar to speed up tests, we rely on
@@ -188,26 +188,32 @@ def test_render_default(self):
self.failIf(browser.is_element_present('css=.errorMsgLbl'))
def test_type_bad_input(self):
+ import time
browser.open(self.url)
browser.wait_for_page_to_load("30000")
browser.focus('deformField1')
browser.key_press('deformField1', 'a')
+ time.sleep(.005)
browser.focus('deformField1-confirm')
browser.key_press('deformField1-confirm', 'a')
+ time.sleep(.005)
self.failUnless(
browser.get_value('deformField1') in ('', '###-##-####'))
self.failUnless(
browser.get_value('deformField1-confirm') in ('', '###-##-####'))
def test_submit_success(self):
+ import time
browser.open(self.url)
browser.wait_for_page_to_load("30000")
browser.focus('deformField1')
for key in '140118866':
browser.key_press('deformField1', key)
+ time.sleep(.005)
browser.focus('deformField1-confirm')
for key in '140118866':
browser.key_press('deformField1-confirm', key)
+ time.sleep(.005)
browser.click('submit')
browser.wait_for_page_to_load("30000")
self.assertEqual(browser.get_text('css=#captured'),
@@ -1691,13 +1697,15 @@ def test_submit_two_unfilled(self):
self.assertEqual(captured, 'None')
def test_submit_one_filled(self):
+ import time
browser.open(self.url)
browser.wait_for_page_to_load("30000")
browser.click('deformField1-seqAdd')
added = 'dom=document.forms[0].text'
browser.focus(added)
for key in '140118866':
browser.key_press(added, key)
+ time.sleep(.005)
browser.click("submit")
browser.wait_for_page_to_load("30000")
self.failIf(browser.is_element_present('css=.errorMsgLbl'))
@@ -2280,31 +2288,62 @@ def test_render_default(self):
self.failIf(browser.is_element_present('css=.errorMsgLbl'))
def test_type_bad_input(self):
+ import time
browser.open(self.url)
browser.wait_for_page_to_load("30000")
browser.focus('deformField1')
browser.key_press('deformField1', 'a')
+ time.sleep(.005)
browser.focus('deformField2')
browser.key_press('deformField2', 'a')
+ time.sleep(.005)
self.failUnless(
browser.get_value('deformField1') in ('', '___-__-____'))
self.failUnless(
browser.get_value('deformField2') in ('', '__/__/____'))
def test_submit_success(self):
+ import time
browser.open(self.url)
browser.wait_for_page_to_load("30000")
browser.focus('deformField1')
for key in '140118866':
browser.key_press('deformField1', key)
+ time.sleep(.005)
browser.focus('deformField2')
for key in '10102010':
browser.key_press('deformField2', key)
+ time.sleep(.005)
browser.click('submit')
browser.wait_for_page_to_load("30000")
self.assertEqual(browser.get_text('css=#captured'),
u"{'date': u'10/10/2010', 'ssn': u'140-11-8866'}")
+class MultipleErrorMessagesInMappingTest(unittest.TestCase):
+ url = "/multiple_error_messages_map/"
+ def test_it(self):
+ browser.open(self.url)
+ browser.wait_for_page_to_load("30000")
+ browser.type('deformField1', 'whatever')
+ browser.click('submit')
+ browser.wait_for_page_to_load("30000")
+ self.assertEqual(browser.get_text('error-deformField1'), 'Error 1')
+ self.assertEqual(browser.get_text('error-deformField1-1'), 'Error 2')
+ self.assertEqual(browser.get_text('error-deformField1-2'), 'Error 3')
+
+class MultipleErrorMessagesInSequenceTest(unittest.TestCase):
+ url = "/multiple_error_messages_seq/"
+ def test_it(self):
+ browser.open(self.url)
+ browser.wait_for_page_to_load("30000")
+ browser.click('deformField1-seqAdd')
+ browser.type('dom=document.forms[0].field', 'whatever')
+ browser.click('submit')
+ browser.wait_for_page_to_load("30000")
+ self.assertEqual(browser.get_text('error-deformField3'), 'Error 1')
+ self.assertEqual(browser.get_text('error-deformField3-1'), 'Error 2')
+ self.assertEqual(browser.get_text('error-deformField3-2'), 'Error 3')
+
if __name__ == '__main__':
setUpModule()
try:
View
2 setup.py
@@ -23,7 +23,7 @@
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
- 'colander>=0.7.2', # widget-arg providing
+ 'colander>=0.7.3', # Invalid.messages()-providing
'chameleon>=1.2.3', # debug arg
'translationstring',
'peppercorn',

0 comments on commit 85d0b67

Please sign in to comment.