1 import unittest
2 from cgi import FieldStorage, MiniFieldStorage
4 from roundup.cgi.templating import *
5 from test_actions import MockNull, true
7 class MockDatabase(MockNull):
8 def getclass(self, name):
9 return self.classes[name]
11 class TemplatingTestCase(unittest.TestCase):
12 def setUp(self):
13 self.form = FieldStorage()
14 self.client = MockNull()
15 self.client.db = db = MockDatabase()
16 db.security.hasPermission = lambda *args, **kw: True
17 self.client.form = self.form
19 class HTMLDatabaseTestCase(TemplatingTestCase):
20 def test_HTMLDatabase___getitem__(self):
21 db = HTMLDatabase(self.client)
22 self.assert_(isinstance(db['issue'], HTMLClass))
23 # following assertions are invalid
24 # since roundup/cgi/templating.py r1.173.
25 # HTMLItem is function, not class,
26 # but HTMLUserClass and HTMLUser are passed on.
27 # these classes are no more. they have ceased to be.
28 #self.assert_(isinstance(db['user'], HTMLUserClass))
29 #self.assert_(isinstance(db['issue1'], HTMLItem))
30 #self.assert_(isinstance(db['user1'], HTMLUser))
32 def test_HTMLDatabase___getattr__(self):
33 db = HTMLDatabase(self.client)
34 self.assert_(isinstance(db.issue, HTMLClass))
35 # see comment in test_HTMLDatabase___getitem__
36 #self.assert_(isinstance(db.user, HTMLUserClass))
37 #self.assert_(isinstance(db.issue1, HTMLItem))
38 #self.assert_(isinstance(db.user1, HTMLUser))
40 def test_HTMLDatabase_classes(self):
41 db = HTMLDatabase(self.client)
42 db._db.classes = {'issue':MockNull(), 'user': MockNull()}
43 db.classes()
45 class FunctionsTestCase(TemplatingTestCase):
46 def test_lookupIds(self):
47 db = HTMLDatabase(self.client)
48 def lookup(key):
49 if key == 'ok':
50 return '1'
51 if key == 'fail':
52 raise KeyError, 'fail'
53 return key
54 db._db.classes = {'issue': MockNull(lookup=lookup)}
55 prop = MockNull(classname='issue')
56 self.assertEqual(lookupIds(db._db, prop, ['1','2']), ['1','2'])
57 self.assertEqual(lookupIds(db._db, prop, ['ok','2']), ['1','2'])
58 self.assertEqual(lookupIds(db._db, prop, ['ok', 'fail'], 1),
59 ['1', 'fail'])
60 self.assertEqual(lookupIds(db._db, prop, ['ok', 'fail']), ['1'])
62 def test_lookupKeys(self):
63 db = HTMLDatabase(self.client)
64 def get(entry, key):
65 return {'1': 'green', '2': 'eggs'}.get(entry, entry)
66 shrubbery = MockNull(get=get)
67 db._db.classes = {'shrubbery': shrubbery}
68 self.assertEqual(lookupKeys(shrubbery, 'spam', ['1','2']),
69 ['green', 'eggs'])
70 self.assertEqual(lookupKeys(shrubbery, 'spam', ['ok','2']), ['ok',
71 'eggs'])
73 class HTMLClassTestCase(TemplatingTestCase) :
75 def test_link(self):
76 """Make sure lookup of a Link property works even in the
77 presence of multiple values in the form."""
78 def lookup(key) :
79 self.assertEqual(key, key.strip())
80 return "Status%s"%key
81 self.form.list.append(MiniFieldStorage("status", "1"))
82 self.form.list.append(MiniFieldStorage("status", "2"))
83 status = hyperdb.Link("status")
84 self.client.db.classes = dict \
85 ( issue = MockNull(getprops = lambda : dict(status = status))
86 , status = MockNull(get = lambda id, name : id, lookup = lookup)
87 )
88 cls = HTMLClass(self.client, "issue")
89 cls["status"]
91 def test_multilink(self):
92 """`lookup` of an item will fail if leading or trailing whitespace
93 has not been stripped.
94 """
95 def lookup(key) :
96 self.assertEqual(key, key.strip())
97 return "User%s"%key
98 self.form.list.append(MiniFieldStorage("nosy", "1, 2"))
99 nosy = hyperdb.Multilink("user")
100 self.client.db.classes = dict \
101 ( issue = MockNull(getprops = lambda : dict(nosy = nosy))
102 , user = MockNull(get = lambda id, name : id, lookup = lookup)
103 )
104 cls = HTMLClass(self.client, "issue")
105 cls["nosy"]
107 def test_url_match(self):
108 '''Test the URL regular expression in StringHTMLProperty.
109 '''
110 def t(s, nothing=False, **groups):
111 m = StringHTMLProperty.hyper_re.search(s)
112 if nothing:
113 if m:
114 self.assertEquals(m, None, '%r matched (%r)'%(s, m.groupdict()))
115 return
116 else:
117 self.assertNotEquals(m, None, '%r did not match'%s)
118 d = m.groupdict()
119 for g in groups:
120 self.assertEquals(d[g], groups[g], '%s %r != %r in %r'%(g, d[g],
121 groups[g], s))
123 #t('123.321.123.321', 'url')
124 t('http://localhost/', url='http://localhost/')
125 t('http://roundup.net/', url='http://roundup.net/')
126 t('http://richard@localhost/', url='http://richard@localhost/')
127 t('http://richard:sekrit@localhost/',
128 url='http://richard:sekrit@localhost/')
129 t('<HTTP://roundup.net/>', url='HTTP://roundup.net/')
130 t('www.a.ex', url='www.a.ex')
131 t('foo.a.ex', nothing=True)
132 t('StDevValidTimeSeries.GetObservation', nothing=True)
133 t('http://a.ex', url='http://a.ex')
134 t('http://a.ex/?foo&bar=baz\\.@!$%()qwerty',
135 url='http://a.ex/?foo&bar=baz\\.@!$%()qwerty')
136 t('www.foo.net', url='www.foo.net')
137 t('richard@com.example', email='richard@com.example')
138 t('r@a.com', email='r@a.com')
139 t('i1', **{'class':'i', 'id':'1'})
140 t('item123', **{'class':'item', 'id':'123'})
141 t('www.user:pass@host.net', email='pass@host.net')
142 t('user:pass@www.host.net', url='user:pass@www.host.net')
143 t('123.35', nothing=True)
144 t('-.3535', nothing=True)
146 def test_url_replace(self):
147 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', '')
148 def t(s): return p.hyper_re.sub(p._hyper_repl, s)
149 ae = self.assertEqual
150 ae(t('item123123123123'), 'item123123123123')
151 ae(t('http://roundup.net/'),
152 '<a href="http://roundup.net/">http://roundup.net/</a>')
153 ae(t('<HTTP://roundup.net/>'),
154 '<<a href="HTTP://roundup.net/">HTTP://roundup.net/</a>>')
155 ae(t('<http://roundup.net/>.'),
156 '<<a href="http://roundup.net/">http://roundup.net/</a>>.')
157 ae(t('<www.roundup.net>'),
158 '<<a href="http://www.roundup.net">www.roundup.net</a>>')
159 ae(t('(www.roundup.net)'),
160 '(<a href="http://www.roundup.net">www.roundup.net</a>)')
161 ae(t('foo http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx bar'),
162 'foo <a href="http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx">'
163 'http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx</a> bar')
164 ae(t('(e.g. http://en.wikipedia.org/wiki/Python_(programming_language))'),
165 '(e.g. <a href="http://en.wikipedia.org/wiki/Python_(programming_language)">'
166 'http://en.wikipedia.org/wiki/Python_(programming_language)</a>)')
167 ae(t('(e.g. http://en.wikipedia.org/wiki/Python_(programming_language)).'),
168 '(e.g. <a href="http://en.wikipedia.org/wiki/Python_(programming_language)">'
169 'http://en.wikipedia.org/wiki/Python_(programming_language)</a>).')
170 ae(t('(e.g. http://en.wikipedia.org/wiki/Python_(programming_language))>.'),
171 '(e.g. <a href="http://en.wikipedia.org/wiki/Python_(programming_language)">'
172 'http://en.wikipedia.org/wiki/Python_(programming_language)</a>)>.')
173 ae(t('(e.g. http://en.wikipedia.org/wiki/Python_(programming_language>)).'),
174 '(e.g. <a href="http://en.wikipedia.org/wiki/Python_(programming_language">'
175 'http://en.wikipedia.org/wiki/Python_(programming_language</a>>)).')
177 '''
178 class HTMLPermissions:
179 def is_edit_ok(self):
180 def is_view_ok(self):
181 def is_only_view_ok(self):
182 def view_check(self):
183 def edit_check(self):
185 def input_html4(**attrs):
186 def input_xhtml(**attrs):
188 class HTMLInputMixin:
189 def __init__(self):
191 class HTMLClass(HTMLInputMixin, HTMLPermissions):
192 def __init__(self, client, classname, anonymous=0):
193 def __repr__(self):
194 def __getitem__(self, item):
195 def __getattr__(self, attr):
196 def designator(self):
197 def getItem(self, itemid, num_re=re.compile('-?\d+')):
198 def properties(self, sort=1):
199 def list(self, sort_on=None):
200 def csv(self):
201 def propnames(self):
202 def filter(self, request=None, filterspec={}, sort=(None,None),
203 def classhelp(self, properties=None, label='(list)', width='500',
204 def submit(self, label="Submit New Entry"):
205 def history(self):
206 def renderWith(self, name, **kwargs):
208 class HTMLItem(HTMLInputMixin, HTMLPermissions):
209 def __init__(self, client, classname, nodeid, anonymous=0):
210 def __repr__(self):
211 def __getitem__(self, item):
212 def __getattr__(self, attr):
213 def designator(self):
214 def is_retired(self):
215 def submit(self, label="Submit Changes"):
216 def journal(self, direction='descending'):
217 def history(self, direction='descending', dre=re.compile('\d+')):
218 def renderQueryForm(self):
220 class HTMLUserPermission:
221 def is_edit_ok(self):
222 def is_view_ok(self):
223 def _user_perm_check(self, type):
225 class HTMLUserClass(HTMLUserPermission, HTMLClass):
227 class HTMLUser(HTMLUserPermission, HTMLItem):
228 def __init__(self, client, classname, nodeid, anonymous=0):
229 def hasPermission(self, permission, classname=_marker):
231 class HTMLProperty(HTMLInputMixin, HTMLPermissions):
232 def __init__(self, client, classname, nodeid, prop, name, value,
233 def __repr__(self):
234 def __str__(self):
235 def __cmp__(self, other):
236 def is_edit_ok(self):
237 def is_view_ok(self):
239 class StringHTMLProperty(HTMLProperty):
240 def _hyper_repl(self, match):
241 def hyperlinked(self):
242 def plain(self, escape=0, hyperlink=0):
243 def stext(self, escape=0):
244 def field(self, size = 30):
245 def multiline(self, escape=0, rows=5, cols=40):
246 def email(self, escape=1):
248 class PasswordHTMLProperty(HTMLProperty):
249 def plain(self):
250 def field(self, size = 30):
251 def confirm(self, size = 30):
253 class NumberHTMLProperty(HTMLProperty):
254 def plain(self):
255 def field(self, size = 30):
256 def __int__(self):
257 def __float__(self):
259 class BooleanHTMLProperty(HTMLProperty):
260 def plain(self):
261 def field(self):
263 class DateHTMLProperty(HTMLProperty):
264 def plain(self):
265 def now(self):
266 def field(self, size = 30):
267 def reldate(self, pretty=1):
268 def pretty(self, format=_marker):
269 def local(self, offset):
271 class IntervalHTMLProperty(HTMLProperty):
272 def plain(self):
273 def pretty(self):
274 def field(self, size = 30):
276 class LinkHTMLProperty(HTMLProperty):
277 def __init__(self, *args, **kw):
278 def __getattr__(self, attr):
279 def plain(self, escape=0):
280 def field(self, showid=0, size=None):
281 def menu(self, size=None, height=None, showid=0, additional=[],
283 class MultilinkHTMLProperty(HTMLProperty):
284 def __init__(self, *args, **kwargs):
285 def __len__(self):
286 def __getattr__(self, attr):
287 def __getitem__(self, num):
288 def __contains__(self, value):
289 def reverse(self):
290 def plain(self, escape=0):
291 def field(self, size=30, showid=0):
292 def menu(self, size=None, height=None, showid=0, additional=[],
294 def make_sort_function(db, classname, sort_on=None):
295 def sortfunc(a, b):
297 def find_sort_key(linkcl):
299 def handleListCGIValue(value):
301 class ShowDict:
302 def __init__(self, columns):
303 def __getitem__(self, name):
305 class HTMLRequest(HTMLInputMixin):
306 def __init__(self, client):
307 def _post_init(self):
308 def updateFromURL(self, url):
309 def update(self, kwargs):
310 def description(self):
311 def __str__(self):
312 def indexargs_form(self, columns=1, sort=1, group=1, filter=1,
313 def indexargs_url(self, url, args):
314 def base_javascript(self):
315 def batch(self):
317 class Batch(ZTUtils.Batch):
318 def __init__(self, client, sequence, size, start, end=0, orphan=0,
319 def __getitem__(self, index):
320 def propchanged(self, property):
321 def previous(self):
322 def next(self):
324 class TemplatingUtils:
325 def __init__(self, client):
326 def Batch(self, sequence, size, start, end=0, orphan=0, overlap=0):
328 class NoTemplate(Exception):
329 class Unauthorised(Exception):
330 def __init__(self, action, klass):
331 def __str__(self):
332 def find_template(dir, name, extension):
334 class Templates:
335 def __init__(self, dir):
336 def precompileTemplates(self):
337 def get(self, name, extension=None):
338 def __getitem__(self, name):
340 class RoundupPageTemplate(PageTemplate.PageTemplate):
341 def getContext(self, client, classname, request):
342 def render(self, client, classname, request, **options):
343 def __repr__(self):
344 '''
347 def test_suite():
348 suite = unittest.TestSuite()
349 suite.addTest(unittest.makeSuite(HTMLDatabaseTestCase))
350 suite.addTest(unittest.makeSuite(FunctionsTestCase))
351 suite.addTest(unittest.makeSuite(HTMLClassTestCase))
352 return suite
354 if __name__ == '__main__':
355 runner = unittest.TextTestRunner()
356 unittest.main(testRunner=runner)
358 # vim: set et sts=4 sw=4 :