summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 32eb14f)
raw | patch | inline | side by side (parent: 32eb14f)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Wed, 12 Feb 2003 06:41:58 +0000 (06:41 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Wed, 12 Feb 2003 06:41:58 +0000 (06:41 +0000) |
and creation of multiple items (but only one per class)
- the colon ":" special form variable designator may now be any of : + @
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1498 57a73879-2fb5-44c3-a270-3262357dd7e2
- the colon ":" special form variable designator may now be any of : + @
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1498 57a73879-2fb5-44c3-a270-3262357dd7e2
CHANGES.txt | patch | blob | history | |
roundup/cgi/client.py | patch | blob | history | |
test/test_cgi.py | patch | blob | history |
diff --git a/CHANGES.txt b/CHANGES.txt
index 4feb8abf3f40e43f9cd975e19ce2e545f9d430a4..cc816c3e8e538f3ffc36e479ac08ead8f5ec83eb 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
- fixed error in indexargs_url (thanks Patrick Ohly)
- fixed getnode (sf bug 684531)
- included UN*X manual pages from Bastian Kleineidam
+- implemented extension to form parsing to allow editing of multiple items
+ and creation of multiple items (but only one per class)
+- the colon ":" special form variable designator may now be any of : + @
2003-??-?? 0.5.6
diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py
index d2cb1a79943066a1cbc68e2e792f513fc9598ca9..ef3d2ad842a647b26dbd0562961e4cf0cfca9a70 100644 (file)
--- a/roundup/cgi/client.py
+++ b/roundup/cgi/client.py
-# $Id: client.py,v 1.78 2003-02-12 00:00:28 richard Exp $
+# $Id: client.py,v 1.79 2003-02-12 06:41:58 richard Exp $
__doc__ = """
WWW request handler (also used in the stand-alone server).
Once a user logs in, they are assigned a session. The Client instance
keeps the nodeid of the session as the "session" attribute.
+
+
+ Special form variables:
+ Note that in various places throughout this code, special form
+ variables of the form :<name> are used. The colon (":") part may
+ actually be one of several characters from the set:
+
+ : @ +
+
'''
+ # special form variables
+ FV_TEMPLATE = re.compile(r'[@+:]template')
+ FV_OK_MESSAGE = re.compile(r'[@+:]ok_message')
+ FV_ERROR_MESSAGE = re.compile(r'[@+:]error_message')
+ FV_REQUIRED = re.compile(r'[@+:]required')
+ FV_LINK = re.compile(r'[@+:]link')
+ FV_MULTILINK = re.compile(r'[@+:]multilink')
+ FV_NOTE = re.compile(r'[@+:]note')
+ FV_FILE = re.compile(r'[@+:]file')
+ FV_ADD = re.compile(r'([@+:])add\1')
+ FV_REMOVE = re.compile(r'([@+:])remove\1')
+ FV_CONFIRM = re.compile(r'.+[@+:]confirm')
+ FV_SPLITTER = re.compile(r'[@+:]')
+
def __init__(self, instance, request, env, form=None):
hyperdb.traceMark()
self.instance = instance
self.additional_headers = {}
self.response_code = 200
+
def main(self):
''' Wrap the real main in a try/finally so we always close off the db.
'''
self.classname = None
self.nodeid = None
+ # see if a template or messages are specified
+ template_override = ok_message = error_message = None
+ for key in self.form.keys():
+ if self.FV_TEMPLATE.match(key):
+ template_override = self.form[key].value
+ elif self.FV_OK_MESSAGE.match(key):
+ ok_message = self.form[key].value
+ elif self.FV_ERROR_MESSAGE.match(key):
+ error_message = self.form[key].value
+
# determine the classname and possibly nodeid
path = self.path.split('/')
if not path or path[0] in ('', 'home', 'index'):
- if self.form.has_key(':template'):
- self.template = self.form[':template'].value
+ if template_override is not None:
+ self.template = template_override
else:
self.template = ''
return
raise NotFound, self.classname
# see if we have a template override
- if self.form.has_key(':template'):
- self.template = self.form[':template'].value
+ if template_override is not None:
+ self.template = template_override
# see if we were passed in a message
- if self.form.has_key(':ok_message'):
- self.ok_message.append(self.form[':ok_message'].value)
- if self.form.has_key(':error_message'):
- self.error_message.append(self.form[':error_message'].value)
+ if ok_message:
+ self.ok_message.append(ok_message)
+ if error_message:
+ self.error_message.append(error_message)
def serve_file(self, designator, dre=re.compile(r'([^\d]+)(\d+)')):
''' Serve the file from the content property of the designated item.
# parse the props from the form
try:
- props = parsePropsFromForm(self.db, cl, self.form, self.nodeid)
+ props = self.parsePropsFromForm()
except (ValueError, KeyError), message:
self.error_message.append(_('Error: ') + str(message))
return
# create the new user
cl = self.db.user
try:
- props = parsePropsFromForm(self.db, cl, self.form)
props['roles'] = self.instance.config.NEW_WEB_USER_ROLES
self.userid = cl.create(**props)
self.db.commit()
message = _('You are now registered, welcome!')
# redirect to the item's edit page
- raise Redirect, '%s%s%s?:ok_message=%s'%(
+ raise Redirect, '%s%s%s?+ok_message=%s'%(
self.base, self.classname, self.userid, urllib.quote(message))
def registerPermission(self, props):
"files" property. Attach the file to the message created from
the :note if it's supplied.
- :required=property,property,...
- The named properties are required to be filled in the form.
-
- :remove:<propname>=id(s)
- The ids will be removed from the multilink property.
- :add:<propname>=id(s)
- The ids will be added to the multilink property.
-
+ See parsePropsFromForm for more special variables
'''
- cl = self.db.classes[self.classname]
-
# parse the props from the form
try:
- props = parsePropsFromForm(self.db, cl, self.form, self.nodeid)
+ props = self.parsePropsFromForm()
except (ValueError, KeyError), message:
self.error_message.append(_('Error: ') + str(message))
return
if props:
message = _('%(changes)s edited ok')%{'changes':
', '.join(props.keys())}
- elif self.form.has_key(':note') and self.form[':note'].value:
- message = _('note added')
- elif (self.form.has_key(':file') and self.form[':file'].filename):
- message = _('file added')
else:
message = _('nothing changed')
# redirect to the item's edit page
- raise Redirect, '%s%s%s?:ok_message=%s'%(self.base, self.classname,
+ raise Redirect, '%s%s%s?+ok_message=%s'%(self.base, self.classname,
self.nodeid, urllib.quote(message))
def editItemPermission(self, props):
This follows the same form as the editItemAction, with the same
special form values.
'''
- cl = self.db.classes[self.classname]
-
# parse the props from the form
try:
- props = parsePropsFromForm(self.db, cl, self.form, self.nodeid)
+ props = self.parsePropsFromForm()
except (ValueError, KeyError), message:
self.error_message.append(_('Error: ') + str(message))
return
# redirect to the new item's page
raise Redirect, '%s%s%s?:ok_message=%s'%(self.base, self.classname,
- nid, urllib.quote(message))
+ nid, urllib.quote(message))
def newItemPermission(self, props):
''' Determine whether the user has permission to create (edit) this
link = self.db.classes[link]
link.set(nodeid, **{property: nid})
-def fixNewlines(text):
- ''' Homogenise line endings.
+ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
+ ''' Pull properties for the given class out of the form.
- Different web clients send different line ending values, but
- other systems (eg. email) don't necessarily handle those line
- endings. Our solution is to convert all line endings to LF.
- '''
- text = text.replace('\r\n', '\n')
- return text.replace('\r', '\n')
+ If a ":required" parameter is supplied, then the names
+ property values must be supplied or a ValueError will be raised.
-def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')):
- ''' Pull properties for the given class out of the form.
+ Other special form values:
+ :remove:<propname>=id(s)
+ The ids will be removed from the multilink property.
+ :add:<propname>=id(s)
+ The ids will be added to the multilink property.
- If a ":required" parameter is supplied, then the names property values
- must be supplied or a ValueError will be raised.
+ Note: the colon may be one of: : @ +
- Other special form values:
- :remove:<propname>=id(s)
- The ids will be removed from the multilink property.
- :add:<propname>=id(s)
- The ids will be added to the multilink property.
- '''
- required = []
- if form.has_key(':required'):
- value = form[':required']
- if isinstance(value, type([])):
- required = [i.value.strip() for i in value]
- else:
- required = [i.strip() for i in value.value.split(',')]
-
- props = {}
- keys = form.keys()
- properties = cl.getprops()
- timezone = db.getUserTimezone()
-
- for key in keys:
- # see if we're performing a special multilink action
- mlaction = 'set'
- if key.startswith(':remove:'):
- propname = key[8:]
- mlaction = 'remove'
- elif key.startswith(':add:'):
- propname = key[5:]
- mlaction = 'add'
- else:
- propname = key
-
- # does the property exist?
- if not properties.has_key(propname):
- if mlaction != 'set':
- raise ValueError, 'You have submitted a %s action for'\
- ' the property "%s" which doesn\'t exist'%(mlaction,
- propname)
- continue
- proptype = properties[propname]
-
- # Get the form value. This value may be a MiniFieldStorage or a list
- # of MiniFieldStorages.
- value = form[key]
-
- # handle unpacking of the MiniFieldStorage / list form value
- if isinstance(proptype, hyperdb.Multilink):
- # multiple values are OK
- if isinstance(value, type([])):
- # it's a list of MiniFieldStorages
- value = [i.value.strip() for i in value]
+ Any of the form variables may be prefixed with a classname or
+ designator.
+
+ The return from this method is a dict of
+ classname|designator: properties
+
+ '''
+ # some very useful variables
+ db = self.db
+ form = self.form
+
+ if not hasattr(self, 'FV_CLASSSPEC'):
+ # generate the regexp for detecting
+ # <classname|designator>[@:+]property
+ classes = '|'.join(db.classes.keys())
+ self.FV_CLASSSPEC = re.compile(r'(%s)[@+:](.+)$'%classes)
+ self.FV_ITEMSPEC = re.compile(r'(%s)(\d+)[@+:](.+)$'%classes)
+
+ # these indicate the default class / item
+ default_cn = self.classname
+ default_cl = self.db.classes[default_cn]
+ default_nodeid = str(self.nodeid or '')
+
+ # we'll store info about the individual class/item edit in these
+ all_required = {} # one entry per class/item
+ all_props = {} # one entry per class/item
+ all_propdef = {} # note - only one entry per class
+
+ # we should always return something, even empty, for the context
+ all_props[default_cn+default_nodeid] = {}
+
+ keys = form.keys()
+ timezone = db.getUserTimezone()
+
+ for key in keys:
+ # see if this value modifies a different class/item to the default
+ m = self.FV_CLASSSPEC.match(key)
+ if m:
+ # we got a classname
+ cn = m.group(1)
+ cl = self.db.classes[cn]
+ nodeid = ''
+ propname = m.group(2)
else:
- # it's a MiniFieldStorage, but may be a comma-separated list
- # of values
- value = [i.strip() for i in value.value.split(',')]
+ m = self.FV_ITEMSPEC.match(key)
+ if m:
+ # we got a designator
+ cn = m.group(1)
+ cl = self.db.classes[cn]
+ nodeid = m.group(2)
+ propname = m.group(3)
+ else:
+ # default
+ cn = default_cn
+ cl = default_cl
+ nodeid = default_nodeid
+ propname = key
+
+ # the thing this value relates to is...
+ this = cn+nodeid
+
+ # get more info about the class, and the current set of
+ # form props for it
+ if not all_propdef.has_key(cn):
+ all_propdef[cn] = cl.getprops()
+ propdef = all_propdef[cn]
+ if not all_props.has_key(this):
+ all_props[this] = {}
+ props = all_props[this]
+
+ # detect the special ":required" variable
+ if self.FV_REQUIRED.match(key):
+ value = form[key]
+ if isinstance(value, type([])):
+ required = [i.value.strip() for i in value]
+ else:
+ required = [i.strip() for i in value.value.split(',')]
+ all_required[this] = required
+ continue
- # filter out the empty bits
- value = filter(None, value)
- else:
- # multiple values are not OK
- if isinstance(value, type([])):
- raise ValueError, 'You have submitted more than one value'\
- ' for the %s property'%propname
- # we've got a MiniFieldStorage, so pull out the value and strip
- # surrounding whitespace
- value = value.value.strip()
-
- # handle by type now
- if isinstance(proptype, hyperdb.Password):
- if not value:
- # ignore empty password values
+ # get the required values list
+ if not all_required.has_key(this):
+ all_required[this] = []
+ required = all_required[this]
+
+ # see if we're performing a special multilink action
+ mlaction = 'set'
+ if self.FV_REMOVE.match(propname):
+ propname = propname[8:]
+ mlaction = 'remove'
+ elif self.FV_ADD.match(propname):
+ propname = propname[5:]
+ mlaction = 'add'
+
+ # does the property exist?
+ if not propdef.has_key(propname):
+ if mlaction != 'set':
+ raise ValueError, 'You have submitted a %s action for'\
+ ' the property "%s" which doesn\'t exist'%(mlaction,
+ propname)
continue
- if not form.has_key('%s:confirm'%propname):
- raise ValueError, 'Password and confirmation text do not match'
- confirm = form['%s:confirm'%propname]
- if isinstance(confirm, type([])):
- raise ValueError, 'You have submitted more than one value'\
- ' for the %s property'%propname
- if value != confirm.value:
- raise ValueError, 'Password and confirmation text do not match'
- value = password.Password(value)
-
- elif isinstance(proptype, hyperdb.Link):
- # see if it's the "no selection" choice
- if value == '-1' or not value:
- # if we're creating, just don't include this property
- if not nodeid:
- continue
- value = None
- else:
- # handle key values
- link = proptype.classname
- if not num_re.match(value):
- try:
- value = db.classes[link].lookup(value)
- except KeyError:
- raise ValueError, _('property "%(propname)s": '
- '%(value)s not a %(classname)s')%{
- 'propname': propname, 'value': value,
- 'classname': link}
- except TypeError, message:
- raise ValueError, _('you may only enter ID values '
- 'for property "%(propname)s": %(message)s')%{
- 'propname': propname, 'message': message}
- elif isinstance(proptype, hyperdb.Multilink):
- # perform link class key value lookup if necessary
- link = proptype.classname
- link_cl = db.classes[link]
- l = []
- for entry in value:
- if not entry: continue
- if not num_re.match(entry):
- try:
- entry = link_cl.lookup(entry)
- except KeyError:
- raise ValueError, _('property "%(propname)s": '
- '"%(value)s" not an entry of %(classname)s')%{
- 'propname': propname, 'value': entry,
- 'classname': link}
- except TypeError, message:
- raise ValueError, _('you may only enter ID values '
- 'for property "%(propname)s": %(message)s')%{
- 'propname': propname, 'message': message}
- l.append(entry)
- l.sort()
-
- # now use that list of ids to modify the multilink
- if mlaction == 'set':
- value = l
- else:
- # we're modifying the list - get the current list of ids
- if props.has_key(propname):
- existing = props[propname]
- elif nodeid:
- existing = cl.get(nodeid, propname, [])
+ proptype = propdef[propname]
+
+ # Get the form value. This value may be a MiniFieldStorage or a list
+ # of MiniFieldStorages.
+ value = form[key]
+
+ # handle unpacking of the MiniFieldStorage / list form value
+ if isinstance(proptype, hyperdb.Multilink):
+ # multiple values are OK
+ if isinstance(value, type([])):
+ # it's a list of MiniFieldStorages
+ value = [i.value.strip() for i in value]
else:
- existing = []
+ # it's a MiniFieldStorage, but may be a comma-separated list
+ # of values
+ value = [i.strip() for i in value.value.split(',')]
- # now either remove or add
- if mlaction == 'remove':
- # remove - handle situation where the id isn't in the list
- for entry in l:
+ # filter out the empty bits
+ value = filter(None, value)
+ else:
+ # multiple values are not OK
+ if isinstance(value, type([])):
+ raise ValueError, 'You have submitted more than one value'\
+ ' for the %s property'%propname
+ # we've got a MiniFieldStorage, so pull out the value and strip
+ # surrounding whitespace
+ value = value.value.strip()
+
+ # handle by type now
+ if isinstance(proptype, hyperdb.Password):
+ if not value:
+ # ignore empty password values
+ continue
+ for key in keys:
+ if self.FV_CONFIRM.match(key):
+ confirm = form[key]
+ break
+ else:
+ raise ValueError, 'Password and confirmation text do '\
+ 'not match'
+ if isinstance(confirm, type([])):
+ raise ValueError, 'You have submitted more than one value'\
+ ' for the %s property'%propname
+ if value != confirm.value:
+ raise ValueError, 'Password and confirmation text do '\
+ 'not match'
+ value = password.Password(value)
+
+ elif isinstance(proptype, hyperdb.Link):
+ # see if it's the "no selection" choice
+ if value == '-1' or not value:
+ # if we're creating, just don't include this property
+ if not nodeid:
+ continue
+ value = None
+ else:
+ # handle key values
+ link = proptype.classname
+ if not num_re.match(value):
+ try:
+ value = db.classes[link].lookup(value)
+ except KeyError:
+ raise ValueError, _('property "%(propname)s": '
+ '%(value)s not a %(classname)s')%{
+ 'propname': propname, 'value': value,
+ 'classname': link}
+ except TypeError, message:
+ raise ValueError, _('you may only enter ID values '
+ 'for property "%(propname)s": %(message)s')%{
+ 'propname': propname, 'message': message}
+ elif isinstance(proptype, hyperdb.Multilink):
+ # perform link class key value lookup if necessary
+ link = proptype.classname
+ link_cl = db.classes[link]
+ l = []
+ for entry in value:
+ if not entry: continue
+ if not num_re.match(entry):
try:
- existing.remove(entry)
- except ValueError:
+ entry = link_cl.lookup(entry)
+ except KeyError:
raise ValueError, _('property "%(propname)s": '
- '"%(value)s" not currently in list')%{
- 'propname': propname, 'value': entry}
+ '"%(value)s" not an entry of %(classname)s')%{
+ 'propname': propname, 'value': entry,
+ 'classname': link}
+ except TypeError, message:
+ raise ValueError, _('you may only enter ID values '
+ 'for property "%(propname)s": %(message)s')%{
+ 'propname': propname, 'message': message}
+ l.append(entry)
+ l.sort()
+
+ # now use that list of ids to modify the multilink
+ if mlaction == 'set':
+ value = l
else:
- # add - easy, just don't dupe
- for entry in l:
- if entry not in existing:
- existing.append(entry)
- value = existing
- value.sort()
-
- # other types should be None'd if there's no value
- elif value:
- if isinstance(proptype, hyperdb.String):
- # fix the CRLF/CR -> LF stuff
- value = fixNewlines(value)
- elif isinstance(proptype, hyperdb.Date):
- value = date.Date(value, offset=timezone)
- elif isinstance(proptype, hyperdb.Interval):
- value = date.Interval(value)
- elif isinstance(proptype, hyperdb.Boolean):
- value = value.lower() in ('yes', 'true', 'on', '1')
- elif isinstance(proptype, hyperdb.Number):
- value = float(value)
- else:
- # if we're creating, just don't include this property
- if not nodeid:
- continue
- value = None
+ # we're modifying the list - get the current list of ids
+ if props.has_key(propname):
+ existing = props[propname]
+ elif nodeid:
+ existing = cl.get(nodeid, propname, [])
+ else:
+ existing = []
+
+ # now either remove or add
+ if mlaction == 'remove':
+ # remove - handle situation where the id isn't in
+ # the list
+ for entry in l:
+ try:
+ existing.remove(entry)
+ except ValueError:
+ raise ValueError, _('property "%(propname)s": '
+ '"%(value)s" not currently in list')%{
+ 'propname': propname, 'value': entry}
+ else:
+ # add - easy, just don't dupe
+ for entry in l:
+ if entry not in existing:
+ existing.append(entry)
+ value = existing
+ value.sort()
+
+ # other types should be None'd if there's no value
+ elif value:
+ if isinstance(proptype, hyperdb.String):
+ # fix the CRLF/CR -> LF stuff
+ value = fixNewlines(value)
+ elif isinstance(proptype, hyperdb.Date):
+ value = date.Date(value, offset=timezone)
+ elif isinstance(proptype, hyperdb.Interval):
+ value = date.Interval(value)
+ elif isinstance(proptype, hyperdb.Boolean):
+ value = value.lower() in ('yes', 'true', 'on', '1')
+ elif isinstance(proptype, hyperdb.Number):
+ value = float(value)
+ else:
+ # if we're creating, just don't include this property
+ if not nodeid:
+ continue
+ value = None
- # get the old value
- if nodeid:
- try:
- existing = cl.get(nodeid, propname)
- except KeyError:
- # this might be a new property for which there is no existing
- # value
- if not properties.has_key(propname):
- raise
+ # get the old value
+ if nodeid:
+ try:
+ existing = cl.get(nodeid, propname)
+ except KeyError:
+ # this might be a new property for which there is
+ # no existing value
+ if not propdef.has_key(propname):
+ raise
+
+ # make sure the existing multilink is sorted
+ if isinstance(proptype, hyperdb.Multilink):
+ existing.sort()
+
+ # "missing" existing values may not be None
+ if not existing:
+ if isinstance(proptype, hyperdb.String) and not existing:
+ # some backends store "missing" Strings as empty strings
+ existing = None
+ elif isinstance(proptype, hyperdb.Number) and not existing:
+ # some backends store "missing" Numbers as 0 :(
+ existing = 0
+ elif isinstance(proptype, hyperdb.Boolean) and not existing:
+ # likewise Booleans
+ existing = 0
+
+ # if changed, set it
+ if value != existing:
+ props[propname] = value
+ else:
+ # don't bother setting empty/unset values
+ if value is None:
+ continue
+ elif isinstance(proptype, hyperdb.Multilink) and value == []:
+ continue
+ elif isinstance(proptype, hyperdb.String) and value == '':
+ continue
- # make sure the existing multilink is sorted
- if isinstance(proptype, hyperdb.Multilink):
- existing.sort()
-
- # "missing" existing values may not be None
- if not existing:
- if isinstance(proptype, hyperdb.String) and not existing:
- # some backends store "missing" Strings as empty strings
- existing = None
- elif isinstance(proptype, hyperdb.Number) and not existing:
- # some backends store "missing" Numbers as 0 :(
- existing = 0
- elif isinstance(proptype, hyperdb.Boolean) and not existing:
- # likewise Booleans
- existing = 0
-
- # if changed, set it
- if value != existing:
props[propname] = value
- else:
- # don't bother setting empty/unset values
- if value is None:
- continue
- elif isinstance(proptype, hyperdb.Multilink) and value == []:
- continue
- elif isinstance(proptype, hyperdb.String) and value == '':
- continue
- props[propname] = value
+ # register this as received if required?
+ if propname in required and value is not None:
+ required.remove(propname)
- # register this as received if required?
- if propname in required and value is not None:
- required.remove(propname)
+ # see if all the required properties have been supplied
+ s = []
+ for thing, required in all_required.items():
+ if not required:
+ continue
+ if len(required) > 1:
+ p = 'properties'
+ else:
+ p = 'property'
+ s.append('Required %s %s %s not supplied'%(thing, p,
+ ', '.join(required)))
+ if s:
+ raise ValueError, '\n'.join(s)
- # see if all the required properties have been supplied
- if required:
- if len(required) > 1:
- p = 'properties'
- else:
- p = 'property'
- raise ValueError, 'Required %s %s not supplied'%(p, ', '.join(required))
+ return all_props
- return props
+def fixNewlines(text):
+ ''' Homogenise line endings.
+ Different web clients send different line ending values, but
+ other systems (eg. email) don't necessarily handle those line
+ endings. Our solution is to convert all line endings to LF.
+ '''
+ text = text.replace('\r\n', '\n')
+ return text.replace('\r', '\n')
diff --git a/test/test_cgi.py b/test/test_cgi.py
index cfa4192c9d4b32ba56b27d57468aad50469d3caa..6625b7f090a9ecb495117de5a89eff8766a3a24a 100644 (file)
--- a/test/test_cgi.py
+++ b/test/test_cgi.py
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
-# $Id: test_cgi.py,v 1.6 2003-01-20 23:05:20 richard Exp $
+# $Id: test_cgi.py,v 1.7 2003-02-12 06:41:58 richard Exp $
import unittest, os, shutil, errno, sys, difflib, cgi
form.list.append(cgi.MiniFieldStorage(k, v))
return form
+class config:
+ TRACKER_NAME = 'testing testing'
+ TRACKER_WEB = 'http://testing.testing/'
+
class FormTestCase(unittest.TestCase):
def setUp(self):
self.dirname = '_test_cgi_form'
roles='User', realname='Contrary, Mary')
test = self.instance.dbinit.Class(self.db, "test",
+ string=hyperdb.String(),
boolean=hyperdb.Boolean(), link=hyperdb.Link('test'),
multilink=hyperdb.Multilink('test'), date=hyperdb.Date(),
interval=hyperdb.Interval())
+ def parseForm(self, form, classname='test', nodeid=None):
+ cl = client.Client(self.instance, None, {'PATH_INFO':'/'},
+ makeForm(form))
+ cl.classname = classname
+ cl.nodeid = nodeid
+ cl.db = self.db
+ return cl.parsePropsFromForm()
+
def tearDown(self):
self.db.close()
try:
# Empty form
#
def testNothing(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({})), {})
+ self.assertEqual(self.parseForm({}), {'test': {}})
def testNothingWithRequired(self):
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':required': 'title'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':required': 'title,status',
- 'status':'1'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':required': ['title','status'],
- 'status':'1'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':required': 'status',
- 'status':''}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':required': 'nosy',
- 'nosy':''}))
+ self.assertRaises(ValueError, self.parseForm, {':required': 'string'})
+ self.assertRaises(ValueError, self.parseForm,
+ {':required': 'title,status', 'status':'1'}, 'issue')
+ self.assertRaises(ValueError, self.parseForm,
+ {':required': ['title','status'], 'status':'1'}, 'issue')
+ self.assertRaises(ValueError, self.parseForm,
+ {':required': 'status', 'status':''}, 'issue')
+ self.assertRaises(ValueError, self.parseForm,
+ {':required': 'nosy', 'nosy':''}, 'issue')
#
# Nonexistant edit
#
def testEditNonexistant(self):
- self.assertRaises(IndexError, client.parsePropsFromForm, self.db,
- self.db.test, makeForm({'boolean': ''}), '1')
+ self.assertRaises(IndexError, self.parseForm, {'boolean': ''},
+ 'test', '1')
#
# String
#
def testEmptyString(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'title': ''})), {})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'title': ' '})), {})
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({'title': ['', '']}))
+ self.assertEqual(self.parseForm({'string': ''}), {'test': {}})
+ self.assertEqual(self.parseForm({'string': ' '}), {'test': {}})
+ self.assertRaises(ValueError, self.parseForm, {'string': ['', '']})
def testSetString(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'title': 'foo'})), {'title': 'foo'})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'title': 'a\r\nb\r\n'})), {'title': 'a\nb'})
+ self.assertEqual(self.parseForm({'string': 'foo'}),
+ {'test': {'string': 'foo'}})
+ self.assertEqual(self.parseForm({'string': 'a\r\nb\r\n'}),
+ {'test': {'string': 'a\nb'}})
nodeid = self.db.issue.create(title='foo')
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'title': 'foo'}), nodeid), {})
+ self.assertEqual(self.parseForm({'title': 'foo'}, 'issue', nodeid),
+ {'issue'+nodeid: {}})
def testEmptyStringSet(self):
nodeid = self.db.issue.create(title='foo')
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'title': ''}), nodeid), {'title': None})
+ self.assertEqual(self.parseForm({'title': ''}, 'issue', nodeid),
+ {'issue'+nodeid: {'title': None}})
nodeid = self.db.issue.create(title='foo')
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'title': ' '}), nodeid), {'title': None})
+ self.assertEqual(self.parseForm({'title': ' '}, 'issue', nodeid),
+ {'issue'+nodeid: {'title': None}})
#
# Link
#
def testEmptyLink(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'status': ''})), {})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'status': ' '})), {})
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({'status': ['', '']}))
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'status': '-1'})), {})
+ self.assertEqual(self.parseForm({'link': ''}), {'test': {}})
+ self.assertEqual(self.parseForm({'link': ' '}), {'test': {}})
+ self.assertRaises(ValueError, self.parseForm, {'link': ['', '']})
+ self.assertEqual(self.parseForm({'link': '-1'}), {'test': {}})
def testSetLink(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'status': 'unread'})), {'status': '1'})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'status': '1'})), {'status': '1'})
+ self.assertEqual(self.parseForm({'status': 'unread'}, 'issue'),
+ {'issue': {'status': '1'}})
+ self.assertEqual(self.parseForm({'status': '1'}, 'issue'),
+ {'issue': {'status': '1'}})
nodeid = self.db.issue.create(status='unread')
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'status': 'unread'}), nodeid), {})
+ self.assertEqual(self.parseForm({'status': 'unread'}, 'issue', nodeid),
+ {'issue'+nodeid: {}})
def testUnsetLink(self):
nodeid = self.db.issue.create(status='unread')
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'status': '-1'}), nodeid), {'status': None})
+ self.assertEqual(self.parseForm({'status': '-1'}, 'issue', nodeid),
+ {'issue'+nodeid: {'status': None}})
def testInvalidLinkValue(self):
# XXX This is not the current behaviour - should we enforce this?
-# self.assertRaises(IndexError, client.parsePropsFromForm, self.db,
-# self.db.issue, makeForm({'status': '4'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({'status': 'frozzle'}))
+# self.assertRaises(IndexError, self.parseForm,
+# {'status': '4'}))
+ self.assertRaises(ValueError, self.parseForm, {'link': 'frozzle'})
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.test, makeForm({'link': 'frozzle'}))
+ self.assertRaises(ValueError, self.parseForm, {'link': 'frozzle'})
#
# Multilink
#
def testEmptyMultilink(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': ''})), {})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': ' '})), {})
+ self.assertEqual(self.parseForm({'nosy': ''}), {'test': {}})
+ self.assertEqual(self.parseForm({'nosy': ' '}), {'test': {}})
def testSetMultilink(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': '1'})), {'nosy': ['1']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': 'admin'})), {'nosy': ['1']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': ['1','2']})), {'nosy': ['1','2']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': '1,2'})), {'nosy': ['1','2']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': 'admin,2'})), {'nosy': ['1','2']})
+ self.assertEqual(self.parseForm({'nosy': '1'}, 'issue'),
+ {'issue': {'nosy': ['1']}})
+ self.assertEqual(self.parseForm({'nosy': 'admin'}, 'issue'),
+ {'issue': {'nosy': ['1']}})
+ self.assertEqual(self.parseForm({'nosy': ['1','2']}, 'issue'),
+ {'issue': {'nosy': ['1','2']}})
+ self.assertEqual(self.parseForm({'nosy': '1,2'}, 'issue'),
+ {'issue': {'nosy': ['1','2']}})
+ self.assertEqual(self.parseForm({'nosy': 'admin,2'}, 'issue'),
+ {'issue': {'nosy': ['1','2']}})
def testEmptyMultilinkSet(self):
nodeid = self.db.issue.create(nosy=['1','2'])
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': ''}), nodeid), {'nosy': []})
+ self.assertEqual(self.parseForm({'nosy': ''}, 'issue', nodeid),
+ {'issue'+nodeid: {'nosy': []}})
nodeid = self.db.issue.create(nosy=['1','2'])
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': ' '}), nodeid), {'nosy': []})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': '1,2'}), nodeid), {})
+ self.assertEqual(self.parseForm({'nosy': ' '}, 'issue', nodeid),
+ {'issue'+nodeid: {'nosy': []}})
+ self.assertEqual(self.parseForm({'nosy': '1,2'}, 'issue', nodeid),
+ {'issue'+nodeid: {}})
def testInvalidMultilinkValue(self):
# XXX This is not the current behaviour - should we enforce this?
-# self.assertRaises(IndexError, client.parsePropsFromForm, self.db,
-# self.db.issue, makeForm({'nosy': '4'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({'nosy': 'frozzle'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({'nosy': '1,frozzle'}))
-
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.test, makeForm({'multilink': 'frozzle'}))
+# self.assertRaises(IndexError, self.parseForm,
+# {'nosy': '4'}))
+ self.assertRaises(ValueError, self.parseForm, {'nosy': 'frozzle'},
+ 'issue')
+ self.assertRaises(ValueError, self.parseForm, {'nosy': '1,frozzle'},
+ 'issue')
+ self.assertRaises(ValueError, self.parseForm, {'multilink': 'frozzle'})
def testMultilinkAdd(self):
nodeid = self.db.issue.create(nosy=['1'])
# do nothing
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':add:nosy': ''}), nodeid), {})
+ self.assertEqual(self.parseForm({':add:nosy': ''}, 'issue', nodeid),
+ {'issue'+nodeid: {}})
# do something ;)
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':add:nosy': '2'}), nodeid), {'nosy': ['1','2']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':add:nosy': '2,mary'}), nodeid), {'nosy': ['1','2','4']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':add:nosy': ['2','3']}), nodeid), {'nosy': ['1','2','3']})
+ self.assertEqual(self.parseForm({':add:nosy': '2'}, 'issue', nodeid),
+ {'issue'+nodeid: {'nosy': ['1','2']}})
+ self.assertEqual(self.parseForm({':add:nosy': '2,mary'}, 'issue',
+ nodeid), {'issue'+nodeid: {'nosy': ['1','2','4']}})
+ self.assertEqual(self.parseForm({':add:nosy': ['2','3']}, 'issue',
+ nodeid), {'issue'+nodeid: {'nosy': ['1','2','3']}})
def testMultilinkAddNew(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':add:nosy': ['2','3']})), {'nosy': ['2','3']})
+ self.assertEqual(self.parseForm({':add:nosy': ['2','3']}, 'issue'),
+ {'issue': {'nosy': ['2','3']}})
def testMultilinkRemove(self):
nodeid = self.db.issue.create(nosy=['1','2'])
# do nothing
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':remove:nosy': ''}), nodeid), {})
+ self.assertEqual(self.parseForm({':remove:nosy': ''}, 'issue', nodeid),
+ {'issue'+nodeid: {}})
# do something ;)
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':remove:nosy': '1'}), nodeid), {'nosy': ['2']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':remove:nosy': 'admin,2'}), nodeid), {'nosy': []})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':remove:nosy': ['1','2']}), nodeid), {'nosy': []})
+ self.assertEqual(self.parseForm({':remove:nosy': '1'}, 'issue',
+ nodeid), {'issue'+nodeid: {'nosy': ['2']}})
+ self.assertEqual(self.parseForm({':remove:nosy': 'admin,2'},
+ 'issue', nodeid), {'issue'+nodeid: {'nosy': []}})
+ self.assertEqual(self.parseForm({':remove:nosy': ['1','2']},
+ 'issue', nodeid), {'issue'+nodeid: {'nosy': []}})
# remove one that doesn't exist?
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':remove:nosy': '4'}), nodeid)
+ self.assertRaises(ValueError, self.parseForm, {':remove:nosy': '4'},
+ 'issue', nodeid)
def testMultilinkRetired(self):
self.db.user.retire('2')
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({'nosy': ['2','3']})), {'nosy': ['2','3']})
+ self.assertEqual(self.parseForm({'nosy': ['2','3']}, 'issue'),
+ {'issue': {'nosy': ['2','3']}})
nodeid = self.db.issue.create(nosy=['1','2'])
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':remove:nosy': '2'}), nodeid), {'nosy': ['1']})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.issue,
- makeForm({':add:nosy': '3'}), nodeid), {'nosy': ['1','2','3']})
+ self.assertEqual(self.parseForm({':remove:nosy': '2'}, 'issue',
+ nodeid), {'issue'+nodeid: {'nosy': ['1']}})
+ self.assertEqual(self.parseForm({':add:nosy': '3'}, 'issue', nodeid),
+ {'issue'+nodeid: {'nosy': ['1','2','3']}})
def testAddRemoveNonexistant(self):
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':remove:foo': '2'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.issue, makeForm({':add:foo': '2'}))
+ self.assertRaises(ValueError, self.parseForm, {':remove:foo': '2'},
+ 'issue')
+ self.assertRaises(ValueError, self.parseForm, {':add:foo': '2'},
+ 'issue')
#
# Password
#
def testEmptyPassword(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.user,
- makeForm({'password': ''})), {})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.user,
- makeForm({'password': ''})), {})
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.user, makeForm({'password': ['', '']}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.user, makeForm({'password': 'foo',
- 'password:confirm': ['', '']}))
+ self.assertEqual(self.parseForm({'password': ''}, 'user'),
+ {'user': {}})
+ self.assertEqual(self.parseForm({'password': ''}, 'user'),
+ {'user': {}})
+ self.assertRaises(ValueError, self.parseForm, {'password': ['', '']},
+ 'user')
+ self.assertRaises(ValueError, self.parseForm, {'password': 'foo',
+ 'password:confirm': ['', '']}, 'user')
def testSetPassword(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.user,
- makeForm({'password': 'foo', 'password:confirm': 'foo'})),
- {'password': 'foo'})
+ self.assertEqual(self.parseForm({'password': 'foo',
+ 'password:confirm': 'foo'}, 'user'), {'user': {'password': 'foo'}})
def testSetPasswordConfirmBad(self):
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.user, makeForm({'password': 'foo'}))
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.user, makeForm({'password': 'foo',
- 'password:confirm': 'bar'}))
+ self.assertRaises(ValueError, self.parseForm, {'password': 'foo'},
+ 'user')
+ self.assertRaises(ValueError, self.parseForm, {'password': 'foo',
+ 'password:confirm': 'bar'}, 'user')
def testEmptyPasswordNotSet(self):
nodeid = self.db.user.create(username='1',
password=password.Password('foo'))
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.user,
- makeForm({'password': ''}), nodeid), {})
+ self.assertEqual(self.parseForm({'password': ''}, 'user', nodeid),
+ {'user'+nodeid: {}})
nodeid = self.db.user.create(username='2',
password=password.Password('foo'))
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.user,
- makeForm({'password': '', 'password:confirm': ''}), nodeid), {})
+ self.assertEqual(self.parseForm({'password': '',
+ 'password:confirm': ''}, 'user', nodeid),
+ {'user'+nodeid: {}})
#
# Boolean
#
def testEmptyBoolean(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': ''})), {})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': ' '})), {})
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.test, makeForm({'boolean': ['', '']}))
+ self.assertEqual(self.parseForm({'boolean': ''}), {'test': {}})
+ self.assertEqual(self.parseForm({'boolean': ' '}), {'test': {}})
+ self.assertRaises(ValueError, self.parseForm, {'boolean': ['', '']})
def testSetBoolean(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': 'yes'})), {'boolean': 1})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': 'a\r\nb\r\n'})), {'boolean': 0})
+ self.assertEqual(self.parseForm({'boolean': 'yes'}),
+ {'test': {'boolean': 1}})
+ self.assertEqual(self.parseForm({'boolean': 'a\r\nb\r\n'}),
+ {'test': {'boolean': 0}})
nodeid = self.db.test.create(boolean=1)
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': 'yes'}), nodeid), {})
+ self.assertEqual(self.parseForm({'boolean': 'yes'}, 'test', nodeid),
+ {'test'+nodeid: {}})
nodeid = self.db.test.create(boolean=0)
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': 'no'}), nodeid), {})
+ self.assertEqual(self.parseForm({'boolean': 'no'}, 'test', nodeid),
+ {'test'+nodeid: {}})
def testEmptyBooleanSet(self):
nodeid = self.db.test.create(boolean=0)
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': ''}), nodeid), {'boolean': None})
+ self.assertEqual(self.parseForm({'boolean': ''}, 'test', nodeid),
+ {'test'+nodeid: {'boolean': None}})
nodeid = self.db.test.create(boolean=1)
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'boolean': ' '}), nodeid), {'boolean': None})
+ self.assertEqual(self.parseForm({'boolean': ' '}, 'test', nodeid),
+ {'test'+nodeid: {'boolean': None}})
#
# Date
#
def testEmptyDate(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'date': ''})), {})
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'date': ' '})), {})
- self.assertRaises(ValueError, client.parsePropsFromForm, self.db,
- self.db.test, makeForm({'date': ['', '']}))
+ self.assertEqual(self.parseForm({'date': ''}), {'test': {}})
+ self.assertEqual(self.parseForm({'date': ' '}), {'test': {}})
+ self.assertRaises(ValueError, self.parseForm, {'date': ['', '']})
def testSetDate(self):
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'date': '2003-01-01'})),
- {'date': date.Date('2003-01-01')})
+ self.assertEqual(self.parseForm({'date': '2003-01-01'}),
+ {'test': {'date': date.Date('2003-01-01')}})
nodeid = self.db.test.create(date=date.Date('2003-01-01'))
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'date': '2003-01-01'}), nodeid), {})
+ self.assertEqual(self.parseForm({'date': '2003-01-01'}, 'test',
+ nodeid), {'test'+nodeid: {}})
def testEmptyDateSet(self):
nodeid = self.db.test.create(date=date.Date('.'))
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'date': ''}), nodeid), {'date': None})
+ self.assertEqual(self.parseForm({'date': ''}, 'test', nodeid),
+ {'test'+nodeid: {'date': None}})
nodeid = self.db.test.create(date=date.Date('1970-01-01.00:00:00'))
- self.assertEqual(client.parsePropsFromForm(self.db, self.db.test,
- makeForm({'date': ' '}), nodeid), {'date': None})
+ self.assertEqual(self.parseForm({'date': ' '}, 'test', nodeid),
+ {'test'+nodeid: {'date': None}})
+
+ #
+ # Test multiple items in form
+ #
+ def testMultiple(self):
+ self.assertEqual(self.parseForm({'string': 'a', 'issue@title': 'b'}),
+ {'test': {'string': 'a'}, 'issue': {'title': 'b'}})
+ nodeid = self.db.test.create()
+ self.assertEqual(self.parseForm({'string': 'a', 'issue@title': 'b'},
+ 'test', nodeid),
+ {'test1': {'string': 'a'}, 'issue': {'title': 'b'}})
def suite():
l = [unittest.makeSuite(FormTestCase),