~landscape/zope3/ztk-1.1.3

« back to all changes in this revision

Viewing changes to src/twisted/web/test/test_woven.py

  • Committer: Andreas Hasenack
  • Date: 2009-07-20 17:49:16 UTC
  • Revision ID: andreas@canonical.com-20090720174916-g2tn6qmietz2hn0u
Revert twisted removal, it breaks several dozen tests [trivial]

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
 
 
5
from twisted.trial import unittest
 
6
from twisted.web import server, resource, microdom, domhelpers
 
7
from twisted.web import http
 
8
from twisted.web.test import test_web
 
9
from twisted.internet import reactor, defer, address
 
10
 
 
11
from twisted.web.woven import template, model, view, controller, widgets, input, page, guard
 
12
 
 
13
outputNum = 0
 
14
 
 
15
# Reusable test harness
 
16
 
 
17
class WovenTC(unittest.TestCase):
 
18
    modelFactory = lambda self: None
 
19
    resourceFactory = None
 
20
    def setUp(self):
 
21
        self.m = self.modelFactory()
 
22
        self.t = self.resourceFactory(self.m)
 
23
        self.r = test_web.DummyRequest([''])
 
24
        self.r.prepath = ['']
 
25
        self.prerender()
 
26
        self.t.render(self.r)
 
27
        
 
28
        self.channel = "a fake channel"
 
29
        self.output = ''.join(self.r.written)
 
30
        assert self.output, "No output was generated by the test."
 
31
        global outputNum
 
32
        open("wovenTestOutput%s.html" % (outputNum + 1), 'w').write(self.output)
 
33
        outputNum += 1
 
34
        self.d = microdom.parseString(self.output)
 
35
    
 
36
    def prerender(self):
 
37
        pass
 
38
 
 
39
# Test 1
 
40
# Test that replacing nodes with a string works properly
 
41
 
 
42
 
 
43
class SimpleTemplate(template.DOMTemplate):
 
44
    template = """<http>
 
45
    <head>
 
46
        <title id="title"><span view="getTitle">Hello</span></title>
 
47
    </head>
 
48
    <body>
 
49
        <h3 id="hello"><span view="getHello">Hi</span></h3>
 
50
    </body>
 
51
</http>"""
 
52
    
 
53
    def factory_getTitle(self, request, node):
 
54
        return "Title"
 
55
    
 
56
    def factory_getHello(self, request, node):
 
57
        return "Hello"
 
58
 
 
59
 
 
60
class DOMTemplateTest(WovenTC):
 
61
    resourceFactory = SimpleTemplate
 
62
    def testSimpleRender(self):
 
63
        titleNode = self.d.getElementById("title")
 
64
        helloNode = self.d.getElementById("hello")
 
65
        
 
66
        self.assert_(domhelpers.gatherTextNodes(titleNode) == 'Title')
 
67
        self.assert_(domhelpers.gatherTextNodes(helloNode) == 'Hello')
 
68
 
 
69
 
 
70
# Test 2
 
71
# Test just like the first, but with Text widgets
 
72
 
 
73
class TemplateWithWidgets(SimpleTemplate):
 
74
    def wcfactory_getTitle(self, request, node):
 
75
        return widgets.Text("Title")
 
76
 
 
77
    def wcfactory_getHello(self, request, node):
 
78
        return widgets.Text("Hello")
 
79
 
 
80
 
 
81
class TWWTest(DOMTemplateTest):
 
82
    resourceFactory = TemplateWithWidgets
 
83
 
 
84
 
 
85
# Test 3
 
86
# Test a fancier widget, and controllers handling submitted input
 
87
 
 
88
 
 
89
class MDemo(model.AttributeModel):
 
90
    foo = "Hello world"
 
91
    color = 'blue'
 
92
 
 
93
 
 
94
class FancyBox(widgets.Widget):
 
95
    def setUp(self, request, node, data):
 
96
        self['style'] = 'margin: 1em; padding: 1em; background-color: %s' % data
 
97
 
 
98
 
 
99
class VDemo(view.View):
 
100
    template = """<html>
 
101
 
 
102
<div id="box" model="color" view="FancyBox"></div>
 
103
 
 
104
<form action="">
 
105
Type a color and hit submit:
 
106
<input type="text" controller="change" model="color" name="color" />
 
107
<input type="submit" />
 
108
</form>
 
109
 
 
110
</html>
 
111
"""
 
112
    def wvfactory_FancyBox(self, request, node, model):
 
113
        return FancyBox(model)
 
114
    
 
115
    def renderFailure(self, failure, request):
 
116
        return failure
 
117
 
 
118
 
 
119
class ChangeColor(input.Anything):
 
120
    def commit(self, request, node, data):
 
121
        session = request.getSession()
 
122
        session.color = data
 
123
        self.model.setData(request, data)
 
124
        self.model.notify({'request': request})
 
125
 
 
126
 
 
127
class CDemo(controller.Controller):
 
128
    def setUp(self, request):
 
129
        session = request.getSession()
 
130
        self.model.color = getattr(session, 'color', self.model.color)
 
131
 
 
132
    def wcfactory_change(self, request, node, model):
 
133
        return ChangeColor(model)
 
134
 
 
135
 
 
136
view.registerViewForModel(VDemo, MDemo)
 
137
controller.registerControllerForModel(CDemo, MDemo)
 
138
 
 
139
 
 
140
class ControllerTest(WovenTC):
 
141
    modelFactory = MDemo
 
142
    resourceFactory = CDemo
 
143
    
 
144
    def prerender(self):
 
145
        self.r.addArg('color', 'red')
 
146
    
 
147
    def testControllerOutput(self):
 
148
        boxNode = self.d.getElementById("box")
 
149
        assert boxNode, "Test %s failed" % outputNum
 
150
        style = boxNode.getAttribute("style")
 
151
        styles = style.split(";")
 
152
        sDict = {}
 
153
        for item in styles:
 
154
            key, value = item.split(":")
 
155
            key = key.strip()
 
156
            value = value.strip()
 
157
            sDict[key] = value
 
158
        
 
159
#         print sDict
 
160
        assert sDict['background-color'] == 'red'
 
161
 
 
162
 
 
163
# Test 4
 
164
# Test a list, a list widget, and Deferred data handling
 
165
 
 
166
identityList = ['asdf', 'foo', 'fredf', 'bob']
 
167
 
 
168
class MIdentityList(model.AttributeModel):
 
169
    def __init__(self):
 
170
        model.Model.__init__(self)
 
171
        self.identityList = defer.Deferred()
 
172
        self.identityList.callback(identityList)
 
173
 
 
174
 
 
175
class VIdentityList(view.View):
 
176
    template = """<html>
 
177
    <ul id="list" view="identityList" model="identityList">
 
178
        <li listItemOf="identityList" view="text">
 
179
            Stuff.
 
180
        </li>
 
181
    </ul>
 
182
</html>"""
 
183
 
 
184
    def wvfactory_identityList(self, request, node, model):
 
185
        return widgets.List(model)
 
186
 
 
187
    def wvfactory_text(self, request, node, model):
 
188
        return widgets.Text(model)
 
189
 
 
190
    def renderFailure(self, failure, request):
 
191
        return failure
 
192
 
 
193
 
 
194
class CIdentityList(controller.Controller):
 
195
    pass
 
196
 
 
197
 
 
198
view.registerViewForModel(VIdentityList, MIdentityList)
 
199
controller.registerControllerForModel(CIdentityList, MIdentityList)
 
200
 
 
201
 
 
202
class ListDeferredTest(WovenTC):
 
203
    modelFactory = MIdentityList
 
204
    resourceFactory = CIdentityList
 
205
 
 
206
    def testOutput(self):
 
207
        listNode = self.d.getElementById("list")
 
208
        assert listNode, "Test %s failed; there was no element with the id 'list' in the output" % outputNum
 
209
        liNodes = domhelpers.getElementsByTagName(listNode, 'li')
 
210
        assert len(liNodes) == len(identityList), "Test %s failed; the number of 'li' nodes did not match the list size" % outputNum
 
211
 
 
212
 
 
213
# Test 5
 
214
# Test nested lists
 
215
 
 
216
class LLModel(model.AttributeModel):
 
217
    data = [['foo', 'bar', 'baz'],
 
218
            ['gum', 'shoe'],
 
219
            ['ggg', 'hhh', 'iii']
 
220
           ]
 
221
 
 
222
 
 
223
class LLView(view.View):
 
224
    template = """<html>
 
225
    <ul id="first" view="List" model="data">
 
226
        <li pattern="listItem" view="DefaultWidget">
 
227
            <ol view="List">
 
228
                <li pattern="listItem" view="Text" />
 
229
            </ol>
 
230
        </li>
 
231
    </ul>
 
232
</html>"""
 
233
 
 
234
    def wvfactory_List(self, request, node, model):
 
235
        return widgets.List(model)
 
236
 
 
237
 
 
238
class NestedListTest(WovenTC):
 
239
    modelFactory = LLModel
 
240
    resourceFactory = LLView
 
241
    
 
242
    def testOutput(self):
 
243
        listNode = self.d.getElementById("first")
 
244
        assert listNode, "Test %s failed" % outputNum
 
245
        liNodes = filter(lambda x: hasattr(x, 'tagName') and x.tagName == 'li', listNode.childNodes)
 
246
#        print len(liNodes), len(self.m.data), liNodes, self.m.data
 
247
        assert len(liNodes) == len(self.m.data), "Test %s failed" % outputNum
 
248
        for i in range(len(liNodes)):
 
249
            sublistNode = domhelpers.getElementsByTagName(liNodes[i], 'ol')[0]
 
250
            subLiNodes = domhelpers.getElementsByTagName(sublistNode, 'li')
 
251
            assert len(self.m.data[i]) == len(subLiNodes)
 
252
 
 
253
# Test 6
 
254
# Test notification when a model is a dict or a list
 
255
 
 
256
class MNotifyTest(model.AttributeModel):
 
257
    def initialize(self, *args, **kwargs):
 
258
        self.root = {"inventory": [], 'log': ""}
 
259
 
 
260
 
 
261
class VNotifyTest(view.View):
 
262
    template = """<html>
 
263
    <body>
 
264
        <ol id="theList" model="root/inventory" view="List">
 
265
            <li view="someText" pattern="listItem" />
 
266
        </ol>
 
267
        
 
268
        <form action="">
 
269
            <input model="root" view="DefaultWidget" controller="updateInventory" name="root" />
 
270
            <input type="submit" />
 
271
        </form>
 
272
    </body>
 
273
</html>"""
 
274
 
 
275
    def wvfactory_someText(self, request, node, m):
 
276
        return widgets.Text(m)
 
277
 
 
278
class InventoryUpdater(input.Anything):    
 
279
    def commit(self, request, node, data):
 
280
        invmodel = self.model.getSubmodel(request,  "inventory")
 
281
        log = self.model.getSubmodel(request, "log")
 
282
        inv = invmodel.getData(request)
 
283
        inv.append(data) # just add a string to the list
 
284
        log.setData(request, log.getData(request) + ("%s added to servers\n" % data))
 
285
        invmodel.setData(request, inv)
 
286
        invmodel.notify({'request': request})
 
287
 
 
288
 
 
289
class CNotifyTest(controller.Controller):
 
290
    def wcfactory_updateInventory(self, request, node, model):
 
291
        return InventoryUpdater(model)
 
292
 
 
293
 
 
294
view.registerViewForModel(VNotifyTest, MNotifyTest)
 
295
controller.registerControllerForModel(CNotifyTest, MNotifyTest)
 
296
 
 
297
class NotifyTest(WovenTC):
 
298
    modelFactory = MNotifyTest
 
299
    resourceFactory = CNotifyTest
 
300
 
 
301
    def prerender(self):
 
302
        self.r.addArg('root', 'test')
 
303
 
 
304
    def testComplexNotification(self):
 
305
        listNode = self.d.getElementById("theList")
 
306
        self.assert_(listNode, "Test %s failed" % outputNum)
 
307
        liNodes = domhelpers.getElementsByTagName(listNode, 'li')
 
308
        self.assert_(liNodes,
 
309
         "DOM was not updated by notifying Widgets. Test %s" % outputNum)
 
310
        text = domhelpers.gatherTextNodes(liNodes[0])
 
311
        self.assert_(text.strip() == "test",
 
312
               "Wrong output: %s. Test %s" % (text, outputNum))
 
313
 
 
314
view.registerViewForModel(LLView, LLModel)
 
315
 
 
316
#### Test 7
 
317
# Test model path syntax
 
318
# model="/" should get you the root object
 
319
# model="." should get you the current object
 
320
# model=".." should get you the parent model object
 
321
 
 
322
 
 
323
# xxx sanity check for now; just make sure it doesn't raise anything
 
324
 
 
325
class ModelPathTest(WovenTC):
 
326
    modelFactory = lambda self: ['hello', ['hi', 'there'], 
 
327
                        'hi', ['asdf', ['qwer', 'asdf']]]
 
328
    resourceFactory = page.Page
 
329
 
 
330
    def prerender(self):
 
331
        self.t.template = """<html>
 
332
    <div model="0" view="None">
 
333
        <div model=".." view="Text" />
 
334
    </div>
 
335
    
 
336
    <div model="0" view="None">
 
337
        <div model="../1/../2/../3" view="Text" />
 
338
    </div>
 
339
 
 
340
    <div model="0" view="None">
 
341
        <div model="../3/1/./1" view="Text" />
 
342
    </div>
 
343
    
 
344
    <div model="3/1/0" view="None">
 
345
        <div model="/" view="Text" />
 
346
    </div>
 
347
 
 
348
    <div model="3/1/0" view="None">
 
349
        <div model="/3" view="Text" />
 
350
    </div>
 
351
 
 
352
</html>"""
 
353
 
 
354
#### Test 8
 
355
# Test a large number of widgets
 
356
 
 
357
class HugeTest(WovenTC):
 
358
    modelFactory = lambda self: ['hello' for x in range(100)]
 
359
    resourceFactory = page.Page
 
360
 
 
361
    def prerender(self):
 
362
        self.t.template = """<html>
 
363
    <div model="." view="List">
 
364
        <div pattern="listItem" view="Text" />
 
365
    </div>
 
366
</html>"""
 
367
 
 
368
    def testHugeTest(self):
 
369
        pass
 
370
 
 
371
 
 
372
class ListOfDeferredsTest(WovenTC):
 
373
    """Test rendering a model which is a list of Deferreds."""
 
374
    
 
375
    modelFactory = lambda self: [defer.succeed("hello"), defer.succeed("world")]
 
376
    resourceFactory = page.Page
 
377
 
 
378
    def prerender(self):
 
379
        t = '''<div model="." view="List">
 
380
                  <b pattern="listItem" view="Text" />
 
381
               </div>'''
 
382
        self.t.template = t
 
383
 
 
384
    def testResult(self):
 
385
        n = self.d.firstChild()
 
386
        self.assertEquals(len(n.childNodes), 2)
 
387
        for c in n.childNodes:
 
388
            self.assertEquals(c.nodeName, "b")
 
389
        self.assertEquals(n.firstChild().firstChild().toxml().strip(), "hello")
 
390
        self.assertEquals(n.lastChild().firstChild().toxml().strip(), "world")
 
391
 
 
392
 
 
393
class FakeHTTPChannel:
 
394
    # TODO: this should be an interface in twisted.protocols.http... lots of
 
395
    # things want to fake out HTTP
 
396
    def __init__(self):
 
397
        self.transport = self
 
398
        self.factory = self
 
399
        self.received_cookies = {}
 
400
 
 
401
    # 'factory' attribute needs this
 
402
    def log(self, req):
 
403
        pass
 
404
 
 
405
    # 'channel' of request needs this
 
406
    def requestDone(self, req):
 
407
        self.req = req
 
408
 
 
409
    # 'transport' attribute needs this
 
410
    def getPeer(self):
 
411
        return address.IPv4Address("TCP", "fake", "fake")
 
412
    def getHost(self):
 
413
        return address.IPv4Address("TCP", "fake", 80)
 
414
 
 
415
    def write(self, data):
 
416
        # print data
 
417
        pass
 
418
    def writeSequence(self, datas):
 
419
        for data in datas:
 
420
            self.write(data)
 
421
 
 
422
    # Utility for testing.
 
423
 
 
424
    def makeFakeRequest(self, path, **vars):
 
425
        req = FakeHTTPRequest(self, queued=0)
 
426
        req.received_cookies.update(self.received_cookies)
 
427
        req.requestReceived("GET", path, "1.0")
 
428
        return req
 
429
 
 
430
class FakeHTTPRequest(server.Request):
 
431
    def __init__(self, *args, **kw):
 
432
        server.Request.__init__(self, *args, **kw)
 
433
        self._cookieCache = {}
 
434
        from cStringIO import StringIO
 
435
        self.content = StringIO()
 
436
        self.received_headers['host'] = 'fake.com'
 
437
        self.written = StringIO()
 
438
 
 
439
    def write(self, data):
 
440
        self.written.write(data)
 
441
        server.Request.write(self, data)
 
442
    
 
443
    def addCookie(self, k, v, *args,**kw):
 
444
        server.Request.addCookie(self,k,v,*args,**kw)
 
445
        assert not self._cookieCache.has_key(k), "Should not be setting duplicate cookies!"
 
446
        self._cookieCache[k] = v
 
447
        self.channel.received_cookies[k] = v
 
448
 
 
449
    def processingFailed(self, fail):
 
450
        raise fail
 
451
 
 
452
class FakeSite(server.Site):
 
453
    def getResourceFor(self, req):
 
454
        res = server.Site.getResourceFor(self,req)
 
455
        self.caughtRes = res
 
456
        return res
 
457
 
 
458
from twisted.web import static
 
459
 
 
460
class GuardTest(unittest.TestCase):
 
461
    def testSessionInit(self):
 
462
        sessWrapped = static.Data("you should never see this", "text/plain")
 
463
        swChild = static.Data("NO", "text/plain")
 
464
        sessWrapped.putChild("yyy",swChild)
 
465
        swrap = guard.SessionWrapper(sessWrapped)
 
466
        da = static.Data("b","text/plain")
 
467
        da.putChild("xxx", swrap)
 
468
        st = FakeSite(da)
 
469
        chan = FakeHTTPChannel()
 
470
        chan.site = st
 
471
 
 
472
        # first we're going to make sure that the session doesn't get set by
 
473
        # accident when browsing without first explicitly initializing the
 
474
        # session
 
475
        req = FakeHTTPRequest(chan, queued=0)
 
476
        req.requestReceived("GET", "/xxx/yyy", "1.0")
 
477
        assert len(req._cookieCache.values()) == 0, req._cookieCache.values()
 
478
        self.assertEquals(req.getSession(),None)
 
479
 
 
480
        # now we're going to make sure that the redirect and cookie are properly set
 
481
        req = FakeHTTPRequest(chan, queued=0)
 
482
        req.requestReceived("GET", "/xxx/"+guard.INIT_SESSION, "1.0")
 
483
        ccv = req._cookieCache.values()
 
484
        self.assertEquals(len(ccv),1)
 
485
        cookie = ccv[0]
 
486
        # redirect set?
 
487
        self.failUnless(req.headers.has_key('location'))
 
488
        # redirect matches cookie?
 
489
        self.assertEquals(req.headers['location'].split('/')[-1], guard.SESSION_KEY+cookie)
 
490
        # URL is correct?
 
491
        self.assertEquals(req.headers['location'],
 
492
                          'http://fake.com/xxx/'+guard.SESSION_KEY+cookie)
 
493
        oldreq = req
 
494
        
 
495
        # now let's try with a request for the session-cookie URL that has a cookie set
 
496
        url = "/"+(oldreq.headers['location'].split('http://fake.com/',1))[1]
 
497
        req = chan.makeFakeRequest(url)
 
498
        self.assertEquals(req.headers['location'].split('?')[0],
 
499
                          'http://fake.com/xxx/')
 
500
        for sz in swrap.sessions.values():
 
501
            sz.expire()
 
502
 
 
503
 
 
504
 
 
505
class _TestPage(page.Page):
 
506
    template = """
 
507
        <html><body>
 
508
 
 
509
        <div>First: <span model="title" view="Text"/></div>
 
510
        <div>Second: <span model="title" view="Text"/></div>
 
511
        <div>Third: <span model="title" view="Text"/></div>
 
512
 
 
513
        </body></html>
 
514
    """
 
515
 
 
516
    def wmfactory_title(self, request):
 
517
        d = defer.Deferred()
 
518
        reactor.callLater(0, d.callback, 'The Result')
 
519
        return d
 
520
 
 
521
class DeferredModelTestCase(unittest.TestCase):
 
522
    def testDeferredModel(self):
 
523
        # Test that multiple uses of a deferred model work correctly.
 
524
        channel = FakeHTTPChannel()
 
525
        channel.site = FakeSite(_TestPage())
 
526
        request = channel.makeFakeRequest('/')
 
527
 
 
528
        d = request.notifyFinish()
 
529
        def check(_):
 
530
            dom = microdom.parseXMLString(request.written.getvalue())
 
531
            spanElems = domhelpers.findNodesNamed(dom, 'span')
 
532
            for spanElem in spanElems:
 
533
                self.failUnlessEqual('The Result', spanElem.childNodes[0].data)
 
534
                
 
535
        return d.addCallback(check)
 
536
        
 
537
 
 
538
class MyMacroPage(page.Page):
 
539
    template = """\
 
540
<html macro='foo'>
 
541
<head fill-slot='head'>
 
542
<script>
 
543
<![CDATA[
 
544
             <>'"&
 
545
]]>
 
546
</script>
 
547
</head>
 
548
</html>
 
549
"""
 
550
    def wvfactory_foo(self, request, node, model):
 
551
        return widgets.ExpandMacro(model, macroFile = 'cdataxtester.html',
 
552
                                   macroFileDirectory = '.',
 
553
                                   macroName = 'foo'
 
554
                                   )
 
555
        
 
556
class ExpandMacroTestCase(WovenTC):
 
557
    resourceFactory = MyMacroPage
 
558
    def setUp(self, *args, **kwargs):
 
559
        thepage = """\
 
560
<html>
 
561
<head slot='head' />
 
562
</html>
 
563
"""
 
564
        file('cdatatester.html', 'wb').write(thepage)
 
565
        WovenTC.setUp(self, *args, **kwargs)
 
566
    def testCDATANotQuoted(self):
 
567
        self.failUnless(self.output.find('<>\'"&')>=0)
 
568
 
 
569
 
 
570