[Checkins] SVN: z3c.form/trunk/ HTML output is now compared using a modified version of the XML-aware output checker provided by lxml.

Malthe Borch mborch at gmail.com
Mon Sep 8 09:35:58 EDT 2008


Log message for revision 90946:
  HTML output is now compared using a modified version of the XML-aware output checker provided by lxml.

Changed:
  U   z3c.form/trunk/CHANGES.txt
  U   z3c.form/trunk/setup.py
  U   z3c.form/trunk/src/z3c/form/browser/file-testing.txt
  U   z3c.form/trunk/src/z3c/form/browser/file_testing_input.pt
  U   z3c.form/trunk/src/z3c/form/browser/orderedselect.txt
  U   z3c.form/trunk/src/z3c/form/browser/tests.py
  U   z3c.form/trunk/src/z3c/form/form.txt
  U   z3c.form/trunk/src/z3c/form/group.txt
  U   z3c.form/trunk/src/z3c/form/subform.txt
  U   z3c.form/trunk/src/z3c/form/testing.py
  U   z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt
  U   z3c.form/trunk/src/z3c/form/tests/test_doc.py

-=-
Modified: z3c.form/trunk/CHANGES.txt
===================================================================
--- z3c.form/trunk/CHANGES.txt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/CHANGES.txt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -5,6 +5,9 @@
 Version 2.0.0 (2008-??-??)
 --------------------------
 
+- Refactoring: HTML output is now compared using a modified version of
+  the XML-aware output checker provided by ``lxml``.
+
 - Feature: The `SequenceDataConverter` and `CollectionSequenceDataConverter`
   converter classes now ignore values that are not present in the terms when
   converting to a widget value.

Modified: z3c.form/trunk/setup.py
===================================================================
--- z3c.form/trunk/setup.py	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/setup.py	2008-09-08 13:35:56 UTC (rev 90946)
@@ -82,6 +82,7 @@
             'z3c.coverage',
             'z3c.template',
             'zope.app.i18n',
+            'lxml==2.1.1',
             ],
         adding = ['zope.app.container'],
         ),

Modified: z3c.form/trunk/src/z3c/form/browser/file-testing.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file-testing.txt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/browser/file-testing.txt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -42,7 +42,8 @@
   >>> print widget.render()
   <input type="file" id="widget.id" name="widget.name"
          class="file-widget" />
-  <textarea name="widget.name.testing" style="display: none;"></textarea>
+  <textarea name="widget.name.testing" style="display: none;"><!--
+     nothing here --></textarea>
 
 Let's now make sure that we can extract user entered data from a widget:
 
@@ -90,7 +91,8 @@
   >>> print widget.render()
   <input type="file" id="widget.id" name="widget.name"
          class="file-widget" />
-  <textarea name="widget.name.testing" style="display: none;"></textarea>
+  <textarea name="widget.name.testing" style="display: none;"><!--
+     nothing here --></textarea>
 
 Alternatively, we can also pass in the file upload content via the
 testing text area:

Modified: z3c.form/trunk/src/z3c/form/browser/file_testing_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file_testing_input.pt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/browser/file_testing_input.pt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -1,3 +1,6 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      tal:omit-tag="">
 <input type="file" id="" name="" class="" title="" lang="" disabled=""
        readonly="" alt="" tabindex="" accesskey="" size="" maxlength=""
        tal:attributes="id view/id;
@@ -28,4 +31,5 @@
                        size view/size;
                        maxlength view/maxlength" />
 <textarea name="" style="display: none;"
-       tal:attributes="name string:${view/name}.testing"></textarea>
+       tal:attributes="name string:${view/name}.testing"><!-- nothing here --></textarea>
+</html>

Modified: z3c.form/trunk/src/z3c/form/browser/orderedselect.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/orderedselect.txt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/browser/orderedselect.txt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -19,8 +19,8 @@
 
 The widget can be instantiated only using the request:
 
-  >>> from z3c.form.testing import TestRequest
-  >>> request = TestRequest()
+  >>> from z3c.form import testing
+  >>> request = testing.TestRequest()
 
   >>> widget = orderedselect.OrderedSelectWidget(request)
 
@@ -103,62 +103,40 @@
   ...      interfaces.IOrderedSelectWidget) )
 
 Now let's try if we get widget values:
-
+  
   >>> widget.update()
-  >>> print widget.render()
-  <script type="text/javascript">
-  ...
-  </script>
-  <table border="0" class="ordered-selection-field">
-    <tr>
-      <td>
-        <select id="widget-id-from" name="widget.name.from"
-                size="5" multiple="multiple">
-          <option value="a">A</option>
-          <option value="b">B</option>
-          <option value="c">C</option>
-        </select>
-      </td>
-      ...
-    </tr>
-  </table>
+  >>> print testing.render(widget, './/table//td[1]')
+  <td>
+    <select id="widget-id-from" name="widget.name.from"
+            size="5" multiple="multiple">
+      <option value="a">A</option>
+      <option value="b">B</option>
+      <option value="c">C</option>
+    </select>
+  </td>
 
 If we select item "b", then it should be selected:
 
   >>> widget.value = ['b']
   >>> widget.update()
-  >>> print widget.render()
-  <script type="text/javascript">
-  ...
-  </script>
-  <table border="0" class="ordered-selection-field">
-    <tr>
-      <td>
-        <select id="widget-id-from" name="widget.name.from"
-                size="5" multiple="multiple">
-          <option value="a">A</option>
-          <option value="c">C</option>
-        </select>
-      </td>
-      ...
-      <td>
-        <select id="widget-id-to" name="widget.name.to"
-                size="5" multiple="multiple">
-          <option value="b">B</option>
-        </select>
-        <input name="widget.name-empty-marker" type="hidden" />
-        <span id="widget-id-toDataContainer">
-          <script type="text/javascript">
-            copyDataForSubmit('widget-id');</script>
-        </span>
-      </td>
-      ...
-    </tr>
-  </table>
+  >>> print testing.render(widget, './/table//select[@id="widget-id-from"]/..')
+  <td>
+    <select id="widget-id-from" name="widget.name.from"
+            size="5" multiple="multiple">
+      <option value="a">A</option>
+      <option value="c">C</option>
+    </select>
+  </td>
 
+  >>> print testing.render(widget, './/table//select[@id="widget-id-to"]')
+  <select id="widget-id-to" name="widget.name.to"
+          size="5" multiple="multiple">
+    <option value="b">B</option>
+  </select>
+
 Let's now make sure that we can extract user entered data from a widget:
 
-  >>> widget.request = TestRequest(form={'widget.name': ['c']})
+  >>> widget.request = testing.TestRequest(form={'widget.name': ['c']})
   >>> widget.update()
   >>> widget.extract()
   ['c']
@@ -167,14 +145,14 @@
 the request, but simply no entry at all. For this we have the empty marker, so
 that:
 
-  >>> widget.request = TestRequest(form={'widget.name-empty-marker': '1'})
+  >>> widget.request = testing.TestRequest(form={'widget.name-empty-marker': '1'})
   >>> widget.update()
   >>> widget.extract()
   []
 
 If nothing is found in the request, the default is returned:
 
-  >>> widget.request = TestRequest()
+  >>> widget.request = testing.TestRequest()
   >>> widget.update()
   >>> widget.extract()
   <NOVALUE>
@@ -182,7 +160,7 @@
 Let's now make sure that a bogus value causes extract to return the default as
 described by the interface:
 
-  >>> widget.request = TestRequest(form={'widget.name': ['x']})
+  >>> widget.request = testing.TestRequest(form={'widget.name': ['x']})
   >>> widget.update()
   >>> widget.extract()
   <NOVALUE>
@@ -208,22 +186,13 @@
   ...              SimpleVocabulary.createTerm(CallableValue(3), 'c')
   ...              ])
 
-  >>> widget.terms = SelectionTermsWithCallableValues(None, TestRequest(), None, None, widget)
+  >>> widget.terms = SelectionTermsWithCallableValues(
+  ...     None, testing.TestRequest(), None, None, widget)
   >>> widget.update()
-  >>> print widget.render()
-  <script type="text/javascript">
-  ...
-  </script>
-  <table border="0" class="ordered-selection-field">
-    <tr>
-      <td>
-        <select id="widget-id-from" name="widget.name.from"
-                size="5" multiple="multiple">
-          <option value="a">Callable Value 1</option>
-          <option value="b">Callable Value 2</option>
-          <option value="c">Callable Value 3</option>
-        </select>
-      </td>
-      ...
-    </tr>
-  </table>
+  >>> print testing.render(widget, './/table//select[@id="widget-id-from"]')
+  <select id="widget-id-from" name="widget.name.from"
+          size="5" multiple="multiple">
+    <option value="a">Callable Value 1</option>
+    <option value="b">Callable Value 2</option>
+    <option value="c">Callable Value 3</option>
+  </select>

Modified: z3c.form/trunk/src/z3c/form/browser/tests.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/tests.py	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/browser/tests.py	2008-09-08 13:35:56 UTC (rev 90946)
@@ -11,70 +11,88 @@
 
 import doctest
 import unittest
+
 from zope.testing.doctestunit import DocFileSuite
 
 from z3c.form import testing
 
 def test_suite():
+    checker = testing.OutputChecker()
+
     return unittest.TestSuite((
         DocFileSuite('README.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('button.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('checkbox.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('file.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('file-testing.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('image.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('orderedselect.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('password.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('radio.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('select.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('submit.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('text.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('textarea.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('textlines.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         DocFileSuite('multi.txt',
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
                      ),
         ))

Modified: z3c.form/trunk/src/z3c/form/form.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/form.txt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/form.txt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -475,7 +475,7 @@
 
 and see how the form looks like:
 
-  >>> print addForm.render()
+  >>> print addForm.render() # doctest: +NOPARSE_MARKUP
   <html>
     <body>
       <i>There were some errors.</i>
@@ -587,7 +587,7 @@
 
 Now the factory will be used to provide a template:
 
-  >>> print addForm.render()
+  >>> print addForm.render() # doctest: +NOPARSE_MARKUP
   <html>
   ...
   </html>
@@ -596,7 +596,7 @@
 you call it will invoke both the ``update()`` and ``render()``
 methods:
 
-  >>> print addForm()
+  >>> print addForm() # doctest: +NOPARSE_MARKUP
   <html>
   ...
   </html>
@@ -626,17 +626,13 @@
   >>> addForm = PersonAddForm(root, TestRequest())
   >>> addTemplate(addForm)
   >>> addForm.update()
-  >>> print addForm.render()
-  <html>
-  ...
+  >>> print testing.render(addForm, './/div[2][@class="row"]')
   <div class="row">
     <label for="form-widgets-name">Full Name</label>
-    <input type="text" id="form-widgets-name" name="form.widgets.name"
-           class="text-widget required textline-field" value="" />
+    <input class="text-widget required textline-field"
+           id="form-widgets-name" name="form.widgets.name" type="text" value="">
   </div>
-  ...
 
-
 Adding a "Cancel" button
 ------------------------
 
@@ -673,9 +669,7 @@
 the add form should display a cancel button:
 
   >>> addForm.update()
-  >>> print addForm.render()
-  <html>
-  ...
+  >>> print testing.render(addForm, './/div[@class="action"]')
   <div class="action">
     <input type="submit" id="form-buttons-add" name="form.buttons.add"
            class="submit-widget button-field" value="Add" />
@@ -684,7 +678,6 @@
     <input type="submit" id="form-buttons-cancel" name="form.buttons.cancel"
            class="submit-widget button-field" value="Cancel" />
   </div>
-  ...
 
 But showing the button does not mean it does anything. So we also need a
 custom action handler to handle the cancel action:
@@ -869,11 +862,8 @@
   >>> editForm = PersonEditForm(root[u'srichter'], request)
   >>> addTemplate(editForm)
   >>> editForm.update()
-  >>> print editForm.render()
-  <html>
-  ...
+  >>> print testing.render(editForm, './/i')
   <i>Data successfully updated.</i>
-  ...
 
   >>> stephan = root[u'srichter']
   >>> stephan.name
@@ -935,11 +925,8 @@
   >>> editForm = PersonEditForm(root[u'srichter'], request)
   >>> addTemplate(editForm)
   >>> editForm.update()
-  >>> print editForm.render()
-  <html>
-  ...
+  >>> print testing.render(editForm, './/i')
   <i>No changes were applied.</i>
-  ...
 
 
 Changing Status Messages
@@ -963,11 +950,8 @@
 
   >>> editForm.noChangesMessage = u'No changes were detected in the form data.'
   >>> editForm.update()
-  >>> print editForm.render()
-  <html>
-  ...
+  >>> print testing.render(editForm, './/i')
   <i>No changes were detected in the form data.</i>
-  ...
 
 When even more flexibility is required within a project, one could also
 implement these messages as properties looking up an attribute value. However,
@@ -1302,13 +1286,10 @@
   >>> myEdit = MyEditForm(root[u'srichter'], TestRequest())
   >>> addTemplate(myEdit)
   >>> myEdit.update()
-  >>> print myEdit.render()
-  ...
-  <html...
+  >>> print testing.render(myEdit, './/input[@id="form-widgets-name"]')
   <input type="text" id="form-widgets-name"
          name="form.widgets.name" class="MyCSS required textline-field"
          value="Claudia Richter" />
-  ...
 
 
 Hidden fields
@@ -1332,16 +1313,10 @@
   >>> hiddenEdit = HiddenFieldEditForm(root[u'srichter'], TestRequest())
   >>> addTemplate(hiddenEdit)
   >>> hiddenEdit.update()
-  >>> print hiddenEdit.render()
-  <html>...
-  <input type="text" id="form-widgets-name"
-         name="form.widgets.name" class="MyCSS required textline-field"
-         value="Claudia Richter" />
-  ...
+  >>> print testing.render(hiddenEdit, './/input[@id="form-widgets-age"]')
   <input type="hidden" id="form-widgets-age"
          name="form.widgets.age" class="hidden-widget"
          value="29" />
-  ...
 
 
 Actions with Errors

Modified: z3c.form/trunk/src/z3c/form/group.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/group.txt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/group.txt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -123,7 +123,7 @@
                  class="text-widget required textline-field"
                  value="" />
         </div>
-        <fieldgroup>
+        <fieldset>
           <legend>License</legend>
           <div class="row">
             <label for="form-widgets-license">License</label>
@@ -139,8 +139,8 @@
                    class="text-widget required textline-field"
                    value="" />
           </div>
-        </fieldgroup>
-        <fieldgroup>
+        </fieldset>
+        <fieldset>
           <legend>Car</legend>
           <div class="row">
             <label for="form-widgets-model">Model</label>
@@ -163,7 +163,7 @@
                    class="text-widget required int-field"
                    value="" />
           </div>
-        </fieldgroup>
+        </fieldset>
         <div class="action">
           <input type="submit" id="form-buttons-add"
                  name="form.buttons.add" class="submit-widget button-field"
@@ -187,26 +187,16 @@
 
   >>> add = RegistrationAddForm(None, request)
   >>> add.update()
-  >>> print add.render()
-  <html>
-    <body>
-      <i>There were some errors.</i>
-      <form action=".">
-        ...
-        <fieldgroup>
-          <legend>License</legend>
-          <ul>
-            <li>
-              Address: <div class="error">Required input is missing.</div>
-            </li>
-          </ul>
-          ...
-        </fieldgroup>
-        ...
-      </form>
-    </body>
-  </html>
+  >>> print testing.render(add, './/i')
+  <i>There were some errors.</i>
 
+  >>> print testing.render(add, './/fieldset[1]/ul')
+  <ul>
+    <li>
+      Address: <div class="error">Required input is missing.</div>
+    </li>
+  </ul>
+
 As you can see, the template is clever enough to just report the errors at the
 top of the form, but still report the actual problem within the group. So what
 happens, if errors happen inside and outside a group?
@@ -222,31 +212,30 @@
 
   >>> add = RegistrationAddForm(None, request)
   >>> add.update()
-  >>> print add.render()
-  <html>
-    <body>
-      <i>There were some errors.</i>
-      <ul>
-        <li>
-          Last Name: <div class="error">Required input is missing.</div>
-        </li>
-      </ul>
-      <form action=".">
-        ...
-        <fieldgroup>
-          <legend>License</legend>
-          <ul>
-            <li>
-              Address: <div class="error">Required input is missing.</div>
-            </li>
-          </ul>
-          ...
-        </fieldgroup>
-        ...
-      </form>
-    </body>
-  </html>
+  >>> print testing.render(add, './/i')
+  <i>There were some errors.</i>
 
+  >>> print testing.render(add, './/ul[1]')
+  <ul>
+    <li>
+    Last Name:
+      <div class="error">Required input is missing.</div>
+    </li>
+  </ul>
+  <ul>
+    <li>
+    Address:
+      <div class="error">Required input is missing.</div>
+    </li>
+  </ul>
+  
+  >>> print testing.render(add, './/fieldset[1]/ul')
+  <ul>
+    <li>
+      Address: <div class="error">Required input is missing.</div>
+    </li>
+  </ul>
+
 Let's now successfully complete the add form.
 
   >>> from zope.app.container import btree
@@ -319,7 +308,7 @@
                  class="text-widget required textline-field"
                  value="Richter" />
          </div>
-        <fieldgroup>
+        <fieldset>
           <legend>License</legend>
           <div class="row">
             <label for="form-widgets-license">License</label>
@@ -335,8 +324,8 @@
                    class="text-widget required textline-field"
                    value="10 Main St, Maynard, MA" />
           </div>
-        </fieldgroup>
-        <fieldgroup>
+        </fieldset>
+        <fieldset>
           <legend>Car</legend>
           <div class="row">
             <label for="form-widgets-model">Model</label>
@@ -359,7 +348,7 @@
                    class="text-widget required int-field"
                    value="2005" />
           </div>
-        </fieldgroup>
+        </fieldset>
         <div class="action">
           <input type="submit" id="form-buttons-apply"
                  name="form.buttons.apply" class="submit-widget button-field"
@@ -383,26 +372,24 @@
 
   >>> edit = RegistrationEditForm(reg, request)
   >>> edit.update()
-  >>> print edit.render()
-  <html>
-    <body>
-      <i>There were some errors.</i>
-      <form action=".">
-        ...
-        <fieldgroup>
-          <legend>License</legend>
-          <ul>
-            <li>
-              Address: <div class="error">Required input is missing.</div>
-            </li>
-          </ul>
-          ...
-        </fieldgroup>
-        ...
-      </form>
-    </body>
-  </html>
+  >>> print testing.render(edit, './/i')
+  <i>There were some errors.</i>
 
+  >>> print testing.render(edit, './/ul')
+  <ul>
+    <li>
+    Address:
+      <div class="error">Required input is missing.</div>
+    </li>
+  </ul>
+  
+  >>> print testing.render(edit, './/fieldset/ul')
+  <ul>
+    <li>
+      Address: <div class="error">Required input is missing.</div>
+    </li>
+  </ul>
+
 When an edit form with groups is successfully committed, a detailed 
 object-modified event is sent out telling the system about the changes.
  To see the error, let's create an event subscriber for object-modified events:
@@ -433,13 +420,8 @@
 
 The success message will be shown on the form, ...
 
-  >>> print edit.render()
-  <html>
-    <body>
-      <i>Data successfully updated.</i>
-      ...
-    </body>
-  </html>
+  >>> print testing.render(edit, './/i')
+  <i>Data successfully updated.</i>
 
 and the data is correctly updated:
 
@@ -596,7 +578,7 @@
                  name="form.widgets.year"
                  class="text-widget required int-field" value="2005" />
         </div>
-        <fieldgroup>
+        <fieldset>
           <legend>Owner</legend>
           <div class="row">
             <label for="form-widgets-owner-firstName">First Name</label>
@@ -612,7 +594,7 @@
                    class="text-widget required textline-field"
                    value="Richter" />
           </div>
-        </fieldgroup>
+        </fieldset>
         <div class="action">
           <input type="submit" id="form-buttons-apply"
                  name="form.buttons.apply"
@@ -640,14 +622,9 @@
 
 We'll see if everything worked on the form side.
 
-  >>> print edit.render()
-  <html>
-    <body>
-      <i>Data successfully updated.</i>
-      ...
-    </body>
-  </html>
-
+  >>> print testing.render(edit, './/i')
+  <i>Data successfully updated.</i>
+  
 Now the owner object should have updated fields.
 
   >>> reg.owner.firstName

Modified: z3c.form/trunk/src/z3c/form/subform.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/subform.txt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/subform.txt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -465,22 +465,23 @@
   >>> me.license
   u'MA-991723FDG'
 
-  >>> print carForm.render()
-  <html>
-    ...
-    <fieldset>
-      <legend>Owner</legend>
-      <i>There were some errors.</i>
-      <ul>
-        <li>
-          Name: <div class="error">Constraint not satisfied</div>
-        </li>
-      </ul>
+  >>> print carForm.render() # doctest: +NOPARSE_MARKUP
+  <html>       
       ...
-    </fieldset>
-    ...
+      <fieldset>
+        <legend>Owner</legend>
+        <i>There were some errors.</i>
+        <ul>
+           <li>
+             Name:
+             <div class="error">Constraint not satisfied</div>
+           </li>
+        </ul>
+       ...
+     </fieldset>
+     ...
   </html>
-
+  
 If the data did not change, it is also locally reported:
 
   >>> request = TestRequest(form={
@@ -493,14 +494,14 @@
 
   >>> carForm = CarForm(mycar, request)
   >>> carForm.update()
-  >>> print carForm.render()
+  >>> print carForm.render() # doctest: +NOPARSE_MARKUP
   <html>
     ...
-    <fieldset>
+      <fieldset>
       <legend>Owner</legend>
       <i>No changes were applied.</i>
       ...
-    </fieldset>
+      </fieldset>
     ...
   </html>
 

Modified: z3c.form/trunk/src/z3c/form/testing.py
===================================================================
--- z3c.form/trunk/src/z3c/form/testing.py	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/testing.py	2008-09-08 13:35:56 UTC (rev 90946)
@@ -17,21 +17,31 @@
 """
 __docformat__ = 'restructuredtext'
 import os
+import re
+import doctest
 import zope.component
 import zope.interface
 import zope.schema
+import zope.configuration.xmlconfig
+
 from zope.pagetemplate.interfaces import IPageTemplate
 from zope.publisher.browser import TestRequest
 from zope.security.interfaces import IInteraction
 from zope.security.interfaces import ISecurityPolicy
 from zope.security import checker
 from zope.app.testing import setup
+from zope.testing.doctest import register_optionflag
 
 from z3c.form import browser, button, converter, datamanager, error, field
 from z3c.form import form, interfaces, term, validator, widget
 from z3c.form.browser import radio, select, text
 
+import lxml.html
+import lxml.doctestcompare
 
+# register lxml doctest option flags
+lxml.doctestcompare.NOPARSE_MARKUP = register_optionflag('NOPARSE_MARKUP')
+
 class TestingFileUploadDataConverter(converter.FileUploadDataConverter):
     """A special file upload data converter that works with testing."""
     zope.component.adapts(
@@ -42,7 +52,26 @@
             value = self.widget.request.get(self.widget.name+'.testing', '')
         return super(TestingFileUploadDataConverter, self).toFieldValue(value)
 
+class OutputChecker(lxml.doctestcompare.LHTMLOutputChecker):
+    """Doctest output checker which is better equippied to identify
+    HTML markup than the checker from the ``lxml.doctestcompare``
+    module. It also uses the text comparison function from the
+    built-in ``doctest`` module to allow the use of ellipsis."""
+    
+    _repr_re = re.compile(r'^<([A-Z]|[^>]+ (at|object) |[a-z]+ \'[A-Za-z0-9_.]+\'>)')
+    
+    def _looks_like_markup(self, s):
+        s = s.replace('<BLANKLINE>', '\n').strip()
+        return (s.startswith('<')
+                and not self._repr_re.search(s))
 
+    def text_compare(self, want, got, strip):
+        if want is None: want = ""
+        if got is None: got = ""
+        checker = doctest.OutputChecker()
+        return checker.check_output(
+            want, got, doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE)
+        
 class TestRequest(TestRequest):
     zope.interface.implements(interfaces.IFormLayer)
 
@@ -76,14 +105,41 @@
                 return True
         return False
 
+def render(view, xpath='.'):
+    string = view.render()
+    if string == "":
+        return string
 
+    try:
+        root = lxml.etree.fromstring(string)
+    except lxml.etree.XMLSyntaxError:
+        root = lxml.html.fromstring(string)
+        
+    output = ""
+    for node in root.xpath(
+        xpath, namespaces={'xmlns': 'http://www.w3.org/1999/xhtml'}):
+        s = lxml.etree.tounicode(node, pretty_print=True)
+        s = s.replace(' xmlns="http://www.w3.org/1999/xhtml"', ' ')
+        output += s
+
+    if not output:
+        raise ValueError("No elements matched by %s." % repr(xpath))
+        
+    # let's get rid of blank lines
+    output = output.replace('\n\n', '\n')
+
+    # self-closing tags are more readable with a space before the
+    # end-of-tag marker
+    output = output.replace('"/>', '" />')
+
+    return output
+
 def getPath(filename):
     return os.path.join(os.path.dirname(browser.__file__), filename)
 
 def setUp(test):
     test.globs = {'root': setup.placefulSetUp(True)}
 
-
 def setupFormDefaults():
     # Validator adapters
     zope.component.provideAdapter(validator.SimpleFieldValidator)

Modified: z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt	2008-09-08 13:35:56 UTC (rev 90946)
@@ -21,13 +21,13 @@
                tal:content="widget/label" />
         <input type="text" tal:replace="structure widget/render"
       /></div>
-      <fieldgroup tal:condition="view/groups|nothing"
+      <fieldset tal:condition="view/groups|nothing"
                   tal:repeat="view view/groups">
         <legend tal:condition="view/label"
                 tal:content="view/label">Label</legend>
         <div metal:use-macro="template/macros/errors" />
         <div metal:use-macro="template/macros/rows" />
-      </fieldgroup>
+      </fieldset>
       <div class="action"
            tal:condition="view/actions|nothing"
            tal:repeat="action view/actions/values">

Modified: z3c.form/trunk/src/z3c/form/tests/test_doc.py
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/test_doc.py	2008-09-08 11:51:57 UTC (rev 90945)
+++ z3c.form/trunk/src/z3c/form/tests/test_doc.py	2008-09-08 13:35:56 UTC (rev 90946)
@@ -25,90 +25,107 @@
 from z3c.form import testing
 
 def test_suite():
-    checker = renormalizing.RENormalizing([
-        (re.compile(r"(invalid literal for int\(\)) with base 10: '(.*)'"),
-         r'\1: \2'),
-        ])
+    checker = testing.OutputChecker()
+    
     return unittest.TestSuite((
         doctest.DocFileSuite(
             '../action.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../datamanager.txt',
             setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../field.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../value.txt',
             setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../validator.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../term.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../error.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../widget.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../button.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../zcml.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../converter.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
-            checker=checker,
+            checker=renormalizing.RENormalizing([
+                 (re.compile(
+                  r"(invalid literal for int\(\)) with base 10: '(.*)'"),
+                  r'\1: \2'),
+                 ])
             ),
         doctest.DocFileSuite(
             '../form.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../group.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../subform.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../util.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         doctest.DocFileSuite(
             '../adding.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            checker=checker,
             ),
         ))



More information about the Checkins mailing list