1# Copyright (c) 2004 Divmod.
2# See LICENSE for details.
3
4from zope.interface import implements, Interface
5
6from twisted.internet import defer
7from twisted.trial import unittest
8from twisted.trial.util import suppress as SUPPRESS
9from twisted.python.reflect import qual
10
11from nevow import appserver
12from nevow import inevow
13from nevow import context
14from nevow import flat
15from nevow import rend
16from nevow import loaders
17from nevow.stan import slot
18from nevow.tags import directive, p, html, ul, li, span, table, tr, th, td
19from nevow.tags import div, invisible, head, title, strong, body, a
20from nevow import testutil
21from nevow import url
22from nevow import util
23
24import formless
25from formless import webform as freeform
26from formless import annotate
27from formless import iformless
28
29
30def deferredRender(res, request=None):
31    if request is None:
32        request = testutil.FakeRequest()
33        request.d = defer.Deferred()
34
35    d = util.maybeDeferred(res.renderHTTP,
36        context.PageContext(
37            tag=res, parent=context.RequestContext(
38                tag=request)))
39
40    def done(result):
41        if isinstance(result, str):
42            request.write(result)
43        request.d.callback(request.accumulator)
44        return result
45    d.addCallback(done)
46    return request.d
47
48
49class TestPage(unittest.TestCase):
50
51    def test_simple(self):
52        xhtml = '<ul id="nav"><li>one</li><li>two</li><li>three</li></ul>'
53        r = rend.Page(docFactory=loaders.htmlstr(xhtml))
54        return deferredRender(r).addCallback(
55            lambda result: self.assertEquals(result, xhtml))
56    test_simple.suppress = [
57        SUPPRESS(message=
58                 r"\[v0.8\] htmlstr is deprecated because it's buggy. "
59                 "Please start using xmlfile and/or xmlstr.")]
60
61
62
63    def test_extend(self):
64        xhtml = '<ul id="nav"><li>one</li><li>two</li><li>three</li></ul>'
65        class R(rend.Page):
66            docFactory = loaders.htmlstr(xhtml)
67        r = R()
68        return deferredRender(r).addCallback(
69            lambda result: self.assertEquals(result, xhtml))
70    test_extend.suppress = [
71        SUPPRESS(message=
72                 r"\[v0.8\] htmlstr is deprecated because it's buggy. "
73                 "Please start using xmlfile and/or xmlstr.")]
74
75
76    def test_data(self):
77        xhtml = (
78            '<ul id="nav" nevow:data="numbers" nevow:render="sequence">'
79            '<li nevow:pattern="item" nevow:render="string">number</li>'
80            '</ul>')
81
82        class R(rend.Page):
83            docFactory = loaders.htmlstr(xhtml)
84            def data_numbers(self, context, data):
85                return ['one', 'two', 'three']
86        r = R()
87        return deferredRender(r).addCallback(
88            lambda result: self.assertEquals(
89            result,
90            '<ul id="nav"><li>one</li><li>two</li><li>three</li></ul>'))
91    test_data.suppress = [
92        SUPPRESS(message=
93                 r"\[v0.8\] htmlstr is deprecated because it's buggy. "
94                 "Please start using xmlfile and/or xmlstr.")]
95
96
97    def test_noData(self):
98        """Test when data is missing, i.e. self.original is None and no data
99        directives had been used"""
100        class R(rend.Page):
101            docFactory = loaders.htmlstr('<p nevow:render="foo"></p>')
102            def render_foo(self, ctx, data):
103                return ctx.tag.clear()[data]
104        r = R()
105        return deferredRender(r).addCallback(
106            lambda result: self.assertIn('None', result))
107    test_noData.suppress = [
108        SUPPRESS(message=
109                 r"\[v0.8\] htmlstr is deprecated because it's buggy. "
110                 "Please start using xmlfile and/or xmlstr.")]
111
112
113    def test_render(self):
114        xhtml = '<span nevow:render="replace">replace this</span>'
115
116        class R(rend.Page):
117            docFactory = loaders.htmlstr(xhtml)
118            def render_replace(self, context, data):
119                return context.tag.clear()['abc']
120
121        r = R()
122        return deferredRender(r).addCallback(
123            lambda result: self.assertEquals(result, '<span>abc</span>'))
124    test_render.suppress = [
125        SUPPRESS(message=
126                 r"\[v0.8\] htmlstr is deprecated because it's buggy. "
127                 "Please start using xmlfile and/or xmlstr.")]
128
129
130    def test_dataAndRender(self):
131        xhtml = '''
132        <table nevow:data="numbers" nevow:render="sequence">
133        <tr nevow:pattern="header"><th>English</th><th>French</th></tr>
134        <tr nevow:pattern="item" nevow:render="row"><td><nevow:slot name="english"/></td><td><nevow:slot name="french"/></td></tr>
135        </table>
136        '''
137
138        class R(rend.Page):
139            docFactory = loaders.htmlstr(xhtml)
140            def data_numbers(self, context, data):
141                return [
142                    ['one', 'un/une'],
143                    ['two', 'deux'],
144                    ['three', 'trois'],
145                    ]
146            def render_row(self, context, data):
147                context.fillSlots('english', data[0])
148                context.fillSlots('french', data[1])
149                return context.tag
150
151        r = R()
152        d = deferredRender(r)
153        d.addCallback(
154            lambda result: self.assertEquals(
155                result,
156                '<table>'
157                '<tr><th>English</th><th>French</th></tr>'
158                '<tr><td>one</td><td>un/une</td></tr>'
159                '<tr><td>two</td><td>deux</td></tr>'
160                '<tr><td>three</td><td>trois</td></tr>'
161                '</table>'))
162    test_dataAndRender.suppress = [
163        SUPPRESS(message=
164                 r"\[v0.8\] htmlstr is deprecated because it's buggy. "
165                 "Please start using xmlfile and/or xmlstr.")]
166
167
168    def test_stanData(self):
169        class R(rend.Page):
170            def data_numbers(context, data):
171                return ['one', 'two', 'three']
172            tags = ul(data=data_numbers, render=directive('sequence'))[
173                li(pattern='item')[span(render=str)]
174                ]
175            docFactory = loaders.stan(tags)
176
177        r = R()
178        return deferredRender(r).addCallback(
179            lambda result: self.assertEquals(result, '<ul><li>one</li><li>two</li><li>three</li></ul>'))
180
181    def test_stanRender(self):
182
183
184        class R(rend.Page):
185            def render_replace(context, data):
186                return context.tag.clear()['abc']
187            tags = span(render=render_replace)['replace this']
188            docFactory = loaders.stan(tags)
189
190        r = R()
191        return deferredRender(r).addCallback(
192            lambda result: self.assertEquals(result, '<span>abc</span>'))
193
194    def test_stanDataAndRender(self):
195
196        class R(rend.Page):
197
198            def data_numbers(context, data):
199                return [
200                    ['one', 'un/une'],
201                    ['two', 'deux'],
202                    ['three', 'trois'],
203                    ]
204
205            def render_row(context, data):
206                context.fillSlots('english', data[0])
207                context.fillSlots('french', data[1])
208                return context.tag
209
210            tags = table(data=data_numbers, render=directive('sequence'))[
211                tr(pattern='header')[th['English'], th['French']],
212                tr(pattern='item', render=render_row)[td[slot('english')], td[slot('french')]],
213                ]
214
215            docFactory = loaders.stan(tags)
216
217        r = R()
218        return deferredRender(r).addCallback(
219            lambda result: self.assertEquals(result, '<table><tr><th>English</th><th>French</th></tr><tr><td>one</td><td>un/une</td></tr><tr><td>two</td><td>deux</td></tr><tr><td>three</td><td>trois</td></tr></table>'))
220
221    def test_composite(self):
222
223        class R(rend.Page):
224
225            def render_inner(self, context, data):
226                return rend.Page(docFactory=loaders.stan(div(id='inner')))
227
228            docFactory = loaders.stan(
229                div(id='outer')[
230                    span(render=render_inner)
231                    ]
232                )
233        r = R()
234        return deferredRender(r).addCallback(
235            lambda result: self.assertEquals(result, '<div id="outer"><div id="inner"></div></div>'))
236
237    def _testDocFactoryInStanTree(self, docFactory, expected):
238        class Page(rend.Page):
239            docFactory = loaders.stan(div[invisible(render=directive('included'))])
240
241            def __init__(self, included):
242                self.included = included
243                rend.Page.__init__(self)
244
245            def render_included(self, context, data):
246                return self.included
247
248        return deferredRender(Page(docFactory)).addCallback(
249            self.assertEqual, '<div>' + expected + '</div>')
250
251
252    def test_stanInStanTree(self):
253        """
254        """
255        return self._testDocFactoryInStanTree(
256            loaders.stan(p['fee']),
257            '<p>fee</p>')
258
259
260    def test_htmlStringInStanTree(self):
261        """
262        Test that an htmlstr loader in a stan document is flattened by
263        having its document loaded and flattened.
264        """
265        return self._testDocFactoryInStanTree(
266            loaders.htmlstr('<p>fi</p>'),
267            '<p>fi</p>')
268    test_htmlStringInStanTree.suppress = [
269        SUPPRESS(message=
270                 r"\[v0.8\] htmlstr is deprecated because it's buggy. "
271                 "Please start using xmlfile and/or xmlstr.")]
272
273
274    def test_xmlStringInStanTree(self):
275        """
276        Like L{test_htmlStringInStanTree}, but for an xmlstr loader.
277        """
278        return self._testDocFactoryInStanTree(
279            loaders.xmlstr('<p>fo</p>'),
280            '<p>fo</p>')
281
282
283    def test_htmlFileInStanTree(self):
284        """
285        Like L{test_htmlStringInStanTree}, but for an htmlfile loader.
286        """
287        doc = '<p>fum</p>'
288        temp = self.mktemp()
289        f = file(temp, 'w')
290        f.write(doc)
291        f.close()
292
293        return self._testDocFactoryInStanTree(
294            loaders.htmlfile(temp),
295            '<p>fum</p>')
296    test_htmlFileInStanTree.suppress = [
297        SUPPRESS(message=
298                 r"\[v0.8\] htmlfile is deprecated because it's buggy. "
299                 "Please start using xmlfile and/or xmlstr.")]
300
301
302    def test_xmlFileInStanTree(self):
303        """
304        Like L{test_htmlStringInStanTree}, but for an xmlfile loader.
305        """
306        doc = '<p>I</p>'
307        temp = self.mktemp()
308        f = file(temp, 'w')
309        f.write(doc)
310        f.close()
311
312        return self._testDocFactoryInStanTree(
313            loaders.xmlfile(temp),
314            '<p>I</p>')
315
316
317    def test_reusedDocFactory(self):
318        """
319        Test that a docFactory which is used more than once behaves properly
320        both times.
321        """
322        class Page(rend.Page):
323            docFactory = loaders.stan(div[invisible(render=directive('included'))])
324
325            def __init__(self, included):
326                self.included = included
327                rend.Page.__init__(self)
328
329            def render_included(self, context, data):
330                return self.included
331
332        p1 = Page(loaders.stan('first'))
333        p2 = Page(loaders.xmlstr('<p>second</p>'))
334
335        d = deferredRender(p1)
336        def rendered(result):
337            self.assertEqual(result, '<div>first</div>')
338            return deferredRender(p2)
339        d.addCallback(rendered)
340
341        def renderedAgain(result):
342            self.assertEqual(result, '<div><p>second</p></div>')
343        d.addCallback(renderedAgain)
344
345        return d
346
347
348    def test_buffered(self):
349        class Page(rend.Page):
350            buffered = True
351            docFactory = loaders.stan(html[head[title['test']]])
352
353        p = Page()
354        return deferredRender(p).addCallback(
355            lambda result:
356            self.assertEquals(result, '<html><head><title>test</title></head></html>'))
357
358    def test_component(self):
359        """
360        Test that the data is remembered correctly when a Page is embedded in
361        a component-like manner.
362        """
363
364        class Data:
365            foo = 'foo'
366            bar = 'bar'
367
368        class Component(rend.Fragment):
369
370            def render_foo(self, context, data):
371                return strong[data.foo]
372
373            def render_bar(self, context, data):
374                return data.bar
375
376            docFactory = loaders.stan(p[render_foo, ' ', render_bar])
377
378        class Page(rend.Page):
379            docFactory = loaders.stan(div[Component(Data())])
380
381        page = Page()
382        return deferredRender(page).addCallback(
383            lambda result:
384            self.assertEquals(result, '<div><p><strong>foo</strong> bar</p></div>'))
385
386    def test_fragmentContext(self):
387        # A fragment is remembered as the IRendererFactory. It must create a new context
388        # to avoid messing up the page's context.
389
390        class Fragment(rend.Fragment):
391            docFactory = loaders.stan(p(render=directive('foo')))
392            def render_foo(self, ctx, data):
393                return 'foo'
394
395        class Page(rend.Page):
396            docFactory = loaders.stan(
397                    html(render=directive("template"))[
398                        body[
399                            p(render=directive("before")),
400                            p[slot(name="maincontent")],
401                            p(render=directive("after")),
402                        ],
403                    ]
404                )
405
406            def render_before(self,context,data):
407                return context.tag["before"]
408
409            def render_template(self,context,data):
410                context.fillSlots('maincontent', Fragment())
411                return context.tag
412
413            def render_after(self,context,data):
414                return context.tag["after"]
415
416        result = Page().renderSynchronously()
417        # print result
418        self.failIf("'foo' was not found" in result)
419        self.failIf("'after' was not found" in result)
420
421
422    def test_rendererNotFound(self):
423        """
424        An unparameterized renderer which is not defined should render a
425        message about the renderer not being defined.
426
427        Wanted behaviour: missing renderers etc. raise an exception, with some
428        helpful hints on where to locate the typo.
429
430        Feel free to replace test_rendererNotFound{,Parametrized} with unit
431        tests for the exception raising behaviour, if you do implement it.
432        """
433        class Page(rend.Page):
434            docFactory = loaders.stan(html(render=directive("notfound")))
435        page = Page()
436        result = page.renderSynchronously()
437        self.assertEquals(
438            result,
439            "<html>The renderer named 'notfound' was not found in %s.</html>"
440            % util.escapeToXML(repr(page)))
441    test_rendererNotFound.suppress = [
442        SUPPRESS(category=DeprecationWarning,
443            message=("Renderer 'notfound' missing on nevow.test.test_rend.Page"
444                     " will result in an exception."))]
445
446
447    def test_rendererNotFoundParameterized(self):
448        """
449        A parameterized renderer which is not defined should render a message
450        about the renderer not being defined.
451        """
452        class Page(rend.Page):
453            docFactory = loaders.stan(html(render=directive("notfound dummy")))
454        page = Page()
455        result = page.renderSynchronously()
456        self.assertEquals(
457            result,
458            "<html>The renderer named 'notfound' was not found in %s.</html>"
459            % util.escapeToXML(repr(page)))
460    test_rendererNotFoundParameterized.suppress = [
461        SUPPRESS(
462            category=DeprecationWarning,
463            message=("Renderer 'notfound' missing on nevow.test.test_rend.Page"
464                     " will result in an exception."))]
465
466
467    def test_missingRendererDeprecated(self):
468        """
469        Using a renderer which is not defined should emit a deprecation
470        warning.
471        """
472        rendererName = "notfound"
473        class MisdefinedPage(rend.Page):
474            docFactory = loaders.stan(html(render=directive(rendererName)))
475        page = MisdefinedPage()
476        message = "Renderer %r missing on %s will result in an exception."
477        message %= (rendererName, qual(MisdefinedPage))
478        self.assertWarns(
479            DeprecationWarning, message, rend.__file__,
480            page.renderSynchronously)
481    if getattr(unittest.TestCase, 'assertWarns', None) is None:
482        test_missingRendererDeprecated.skip = "TestCase.assertWarns missing"
483
484
485    def test_addSlash(self):
486        """
487        Accessing a resource that has addSlash set via a path without a
488        trailing slash generates a redirect.
489        """
490        class RootPage(rend.Page):
491            def child_child(self, ctx):
492                return SlashyPage()
493
494        class SlashyPage(rend.Page):
495            addSlash = True
496            docFactory = loaders.stan(div(id='inner'))
497
498        root = RootPage()
499        r = self.successResultOf(getResource(root, b'/child'))
500        r.tag.renderHTTP(r)
501        req = inevow.IRequest(r)
502        self.assertTrue(req.redirected_to.endswith(b'/'))
503
504
505
506class TestFragment(unittest.TestCase):
507
508    def test_deprecatedPatternAbuseNonsense(self):
509
510        class Fragment(rend.Fragment):
511            docFactory = loaders.stan(a(href=url.here)['Click!'])
512
513        class Page(rend.Page):
514            docFactory = loaders.stan(Fragment())
515
516        # If this fails, fragment pattern abuse is probably broken again.
517        return deferredRender(Page())
518
519
520class TestRenderFactory(unittest.TestCase):
521
522    def test_dataRenderer(self):
523        ctx = context.WovenContext()
524        ctx.remember(rend.RenderFactory(), inevow.IRendererFactory)
525        self.assertEquals(flat.flatten(p(data='foo', render=directive('data')), ctx), '<p>foo</p>')
526
527class TestConfigurableMixin(unittest.TestCase):
528    def test_formRender(self):
529        class FormPage(rend.Page):
530            bind_test1 = [('foo', annotate.String()), ('bar', annotate.Integer())]
531            bind_test2 = annotate.MethodBinding('test2', annotate.Method(
532                arguments=[annotate.Argument('foo', annotate.String())]))
533
534            bind_test3 = annotate.Property('test3', annotate.Integer())
535
536            def bind_test4(self, ctx):
537                return ([('foo', annotate.String()), ('bar', annotate.Integer())])
538
539            def bind_test5(self, ctx):
540                return annotate.MethodBinding('test5', annotate.Method(
541                    arguments=[annotate.Argument('foo', annotate.String()),
542                               annotate.Argument('bar', annotate.Integer())]))
543
544            docFactory = loaders.stan(html[freeform.renderForms()])
545        return deferredRender(FormPage())
546
547    def test_formRenderDeferred(self):
548        class FormPage(rend.Page):
549            bind_test1 = defer.succeed([('foo', annotate.String()),
550                                        ('bar', annotate.Integer())])
551            bind_test2 = defer.succeed(annotate.MethodBinding('test2', annotate.Method(
552                arguments=[annotate.Argument('foo', annotate.String())])))
553
554            bind_test3 = defer.succeed(annotate.Property('test3', annotate.Integer()))
555
556            def bind_test4(self, ctx):
557                return defer.succeed([('foo', annotate.String()),
558                                       ('bar', annotate.Integer())])
559
560            def bind_test5(self, ctx):
561                return defer.succeed(annotate.MethodBinding('test5', annotate.Method(
562                    arguments=[annotate.Argument('foo', annotate.String()),
563                               annotate.Argument('bar', annotate.Integer())])))
564
565            docFactory = loaders.stan(html[freeform.renderForms()])
566        return deferredRender(FormPage())
567
568
569    def test_formPost(self):
570        class FormPage(rend.Page):
571            bind_test1 = ([('foo', annotate.Integer())])
572            def test1(self, foo):
573                return foo
574
575        ctx = context.WovenContext()
576        result = FormPage().postForm(ctx, 'test1', {'foo': ['42']})
577        return result.addCallback(lambda result: self.assertEquals(result, 42))
578
579    def test_formPostDeferred(self):
580        class FormPage(rend.Page):
581            bind_test1 = defer.succeed(([('foo', annotate.Integer())]))
582            def test1(self, foo):
583                return foo
584
585        ctx = context.WovenContext()
586        result = FormPage().postForm(ctx, 'test1', {'foo': ['42']})
587        return result.addCallback(lambda result: self.assertEquals(result, 42))
588
589    def test_formPostFailure(self):
590        class FormPage(rend.Page):
591            bind_test1 = ([('foo', annotate.Integer())])
592            def test1(self, foo):
593                return foo
594
595        ctx = context.WovenContext()
596        result = FormPage().postForm(ctx, 'test1', {'foo': ['hello, world!']})
597        return self.assertFailure(result, annotate.ValidateError)
598
599    def test_formPostFailureDeferred(self):
600        class FormPage(rend.Page):
601            bind_test1 = defer.succeed(([('foo', annotate.Integer())]))
602            def test1(self, foo):
603                return foo
604
605        ctx = context.WovenContext()
606        result = FormPage().postForm(ctx, 'test1', {'foo': ['hello, world!']})
607        return self.assertFailure(result, annotate.ValidateError)
608
609class IThing(formless.TypedInterface):
610    foo = formless.String()
611
612class Thing:
613    implements(IThing)
614
615class TestLocateConfigurable(unittest.TestCase):
616
617    def test_onSelf(self):
618
619        class Page(rend.Page):
620            implements(IThing)
621            docFactory = loaders.stan(html[freeform.renderForms()])
622
623        page = Page()
624        return deferredRender(page)
625
626    def test_onSelfOriginal(self):
627
628        class Page(rend.Page):
629            docFactory = loaders.stan(html[freeform.renderForms('original')])
630
631        page = Page(Thing())
632        return deferredRender(page)
633
634    def test_onKeyedConfigurable(self):
635
636        class Page(rend.Page):
637
638            def __init__(self):
639                rend.Page.__init__(self)
640                self.thing = Thing()
641
642            def configurable_thing(self, context):
643                return self.thing
644
645            docFactory = loaders.stan(html[freeform.renderForms('thing')])
646
647        page = Page()
648        return deferredRender(page)
649
650
651class TestDeferredDefaultValue(unittest.TestCase):
652    def test_deferredProperty(self):
653        class IDeferredProperty(formless.TypedInterface):
654            d = formless.String()
655
656        from nevow import util
657        deferred = util.Deferred()
658        deferred.callback('the default value')
659        class Implementation(object):
660            implements(IDeferredProperty)
661            d = deferred
662
663        return deferredRender(rend.Page(Implementation(), docFactory=loaders.stan(html[freeform.renderForms('original')]))).addCallback(
664            lambda result: self.assertIn('value="the default value"', result))
665
666
667class TestRenderString(unittest.TestCase):
668
669    def test_simple(self):
670        doc = div[p['foo'],p['bar']]
671        return rend.Page(docFactory=loaders.stan(doc)).renderString().addCallback(
672            lambda result: self.assertEquals(result, '<div><p>foo</p><p>bar</p></div>'))
673
674    def test_parentCtx(self):
675        class IFoo(Interface):
676            pass
677        ctx = context.WovenContext()
678        ctx.remember('Hello!', IFoo)
679        class Page(rend.Page):
680            def render_foo(self, ctx, data):
681                return IFoo(ctx)
682            docFactory = loaders.stan(p[render_foo])
683        return Page().renderString(ctx).addCallback(
684            lambda result:
685            self.assertEquals(
686            result,
687            '<p>Hello!</p>'
688            ))
689
690    def test_remembers(self):
691
692        class Page(rend.Page):
693            docFactory = loaders.stan(
694                html[
695                    body[
696                        p(data=directive('foo'), render=directive('bar'))
697                        ]
698                    ]
699                )
700            def data_foo(self, ctx, data):
701                return 'foo'
702            def render_bar(self, ctx, data):
703                return ctx.tag.clear()[data+'bar']
704
705        return Page().renderString().addCallback(
706            lambda result: self.assertEquals(result, '<html><body><p>foobar</p></body></html>'))
707
708
709class TestRenderSynchronously(unittest.TestCase):
710
711    def test_simple(self):
712
713        doc = div[p['foo'],p['bar']]
714        result = rend.Page(docFactory=loaders.stan(doc)).renderSynchronously()
715        self.assertEquals(result, '<div><p>foo</p><p>bar</p></div>')
716
717    def test_parentCtx(self):
718        class IFoo(Interface):
719            pass
720        ctx = context.WovenContext()
721        ctx.remember('Hello!', IFoo)
722        class Page(rend.Page):
723            def render_foo(self, ctx, data):
724                return IFoo(ctx)
725            docFactory = loaders.stan(p[render_foo])
726        self.assertEquals(Page().renderSynchronously(ctx), '<p>Hello!</p>')
727
728
729def getResource(root, path):
730    return appserver.NevowSite(root).getPageContextForRequestContext(
731            context.RequestContext(
732                tag=testutil.FakeRequest(uri=path)))
733
734
735class TestLocateChild(unittest.TestCase):
736
737    def test_inDict(self):
738        class Child(rend.Page):
739            pass
740        class Parent(rend.Page):
741            pass
742        p = Parent()
743        p.putChild('child', Child())
744        return getResource(p, '/child').addCallback(
745            lambda r: self.failUnless(inevow.IResource.providedBy(r.tag)))
746
747    def test_resourceAttr(self):
748        class Child(rend.Page):
749            pass
750        class Parent(rend.Page):
751            child_child = Child()
752        p = Parent()
753        return getResource(p, '/child').addCallback(
754            lambda r: self.failUnless(inevow.IResource.providedBy(r.tag)))
755
756    def test_methodAttr(self):
757        class Child(rend.Page):
758            pass
759        class Parent(rend.Page):
760            def child_now(self, request):
761                return Child()
762            def child_defer(self, request):
763                return defer.succeed(None).addCallback(lambda x: Child())
764        p = Parent()
765        return self._dotestparent(p)
766
767    def _dotestparent(self, p):
768
769        return defer.DeferredList([
770            getResource(p, '/now').addCallback(
771            lambda r: self.failUnless(inevow.IResource.providedBy(r.tag))),
772
773            getResource(p, '/defer').addCallback(
774
775            lambda r: self.failUnless(inevow.IResource.providedBy(r.tag)))],
776                                  fireOnOneErrback=True)
777
778    def test_childFactory(self):
779        class Child(rend.Page):
780            pass
781        class Parent(rend.Page):
782            def childFactory(self, name, context):
783                if name == 'now':
784                    return Child()
785                if name == 'defer':
786                    return defer.succeed(None).addCallback(lambda x: Child())
787        p = Parent()
788        return self._dotestparent(p)
789
790    def test_oldResource(self):
791        from twisted.web import twcgi
792        class Parent(rend.Page):
793            child_child = twcgi.CGIScript('abc.cgi')
794        p = Parent()
795        return getResource(p, '/child').addCallback(
796            lambda r: self.failUnless(inevow.IResource.providedBy(r.tag)))
797
798    def test_noneChild(self):
799        class Parent(rend.Page):
800            def child_child(self, request):
801                return None
802            def geyDynamicChild(self, name, request):
803                return None
804        p = Parent()
805
806        return defer.DeferredList([
807            getResource(p, '/child').addCallback(
808            lambda r: self.failUnless(isinstance(r.tag, rend.FourOhFour))),
809
810            getResource(p, '/other').addCallback(
811            lambda r: self.failUnless(isinstance(r.tag, rend.FourOhFour))
812            )],
813                                  fireOnOneErrback=True)
814
815    def test_missingRemembrances(self):
816
817        class IThing(Interface):
818            pass
819
820        class Page(rend.Page):
821
822            def render_it(self, ctx, data):
823                return ctx.locate(IThing)
824
825            def child_child(self, ctx):
826                ctx.remember("Thing", IThing)
827                return defer.succeed(Page())
828
829            docFactory = loaders.stan(html[render_it])
830
831        page = Page()
832        return getResource(page, '/child').addCallback(
833            lambda r: self.failUnless(inevow.IResource.providedBy(r.tag)))
834
835    def test_redirectToURL(self):
836        redirectTarget = "http://example.com/bar"
837        class RedirectingPage(rend.Page):
838            def locateChild(self, ctx, segments):
839                return url.URL.fromString(redirectTarget), ()
840
841        page = RedirectingPage()
842        def doAssert(r):
843            ## Render the redirect.
844            r.tag.renderHTTP(r)
845            req = inevow.IRequest(r)
846            self.assertEquals(req.redirected_to, redirectTarget)
847
848        return getResource(page, '/url').addCallback(doAssert)
849
850    def _testRedirecting(self,uchr):
851        class RedirectingPage(rend.Page):
852            def locateChild(self, ctx, segments):
853                return url.URL.fromString(self.original), ()
854
855        page = RedirectingPage(uchr)
856
857        def dotest(r):
858            r.tag.renderHTTP(r)
859            self.assertEquals(uchr,
860                              inevow.IRequest(r).redirected_to)
861
862        return getResource(page, '/url').addCallback(dotest)
863
864
865    def test_redirectQuoting(self):
866        return self._testRedirecting('http://example.com/foo!!bar').addCallback(
867            lambda ign: self._testRedirecting('http://example.com/foo!%40%24bar?b!%40z=123'))
868
869    def test_stringChild(self):
870        theString = "<html>Hello, world</html>"
871        class StringChildPage(rend.Page):
872            def child_foo(self, ctx):
873                return theString
874        page = StringChildPage()
875
876        return getResource(page, '/foo').addCallback(
877            lambda c: deferredRender(c.tag).addCallback(
878            lambda result: self.assertEquals(result, theString)))
879
880    def test_freeformChildMixin_nonTrue(self):
881        """Configurables that have c.__nonzero__()==False are accepted."""
882        class SimpleConf(object):
883            implements(iformless.IConfigurable)
884            # mock mock
885            def postForm(self, ctx, bindingName, args):
886                return 'SimpleConf OK'
887        class FormPage(rend.Page):
888            addSlash = True
889            def configurable_(self, ctx):
890                return SimpleConf()
891        page = FormPage()
892
893        D = getResource(page, '/foo')
894        def x1(r):
895            self.failUnless(isinstance(r.tag, rend.FourOhFour))
896        D.addCallback(x1)
897
898        def x2(ign):
899            D2 = getResource(page, '/freeform_post!!foo')
900            def x3(r):
901                self.failIf(isinstance(r.tag, rend.FourOhFour))
902                return deferredRender(r.tag).addCallback(
903                    lambda result: self.assertEquals(result, 'You posted a form to foo'))
904            D2.addCallback(x3)
905            return D2
906        D.addCallback(x2)
907
908        def x4(ign):
909            SimpleConf.__nonzero__ = lambda x: False
910
911            D3 = getResource(page, '/freeform_post!!foo')
912            def x5(r):
913                self.failIf(isinstance(r.tag, rend.FourOhFour))
914                return deferredRender(r.tag).addCallback(
915                    lambda result:
916                    self.assertEquals(result, 'You posted a form to foo'))
917            return D3.addCallback(x5)
918        D.addCallback(x4)
919        return D
920
921
922class TestStandardRenderers(unittest.TestCase):
923
924    def test_data(self):
925        ctx = context.WovenContext()
926
927        ctx.remember('foo', inevow.IData)
928        tag = p(render=rend.data)
929        self.assertEquals(flat.flatten(tag, ctx), '<p>foo</p>')
930
931        ctx.remember('\xc2\xa3'.decode('utf-8'), inevow.IData)
932        tag = p(render=rend.data)
933        self.assertEquals(flat.flatten(tag, ctx), '<p>\xc2\xa3</p>')
934
935        ctx.remember([1,2,3,4,5], inevow.IData)
936        tag = p(render=rend.data)
937        self.assertEquals(flat.flatten(tag, ctx), '<p>12345</p>')
938
939        ctx.remember({'foo':'bar'}, inevow.IData)
940        tag = p(data=directive('foo'), render=rend.data)
941        self.assertEquals(flat.flatten(tag, ctx), '<p>bar</p>')
942
943class TestMacro(unittest.TestCase):
944
945    def test_macro(self):
946        doc = """
947<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
948    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
949<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xmlns:n="http://nevow.com/ns/nevow/0.1">
950  <body>
951    <n:invisible n:macro="content" />
952  </body>
953</html>
954        """
955        temp = self.mktemp()
956        f = file(temp, 'w')
957        f.write(doc)
958        f.close()
959
960        class Base(rend.Page):
961            docFactory = loaders.xmlfile(temp)
962
963        class Page1(Base):
964            def macro_content(self, ctx):
965                return p["Page1"]
966
967        class Page2(Base):
968            def macro_content(self, ctx):
969                return p["Page2"]
970
971        p1 = Page1()
972        p2 = Page2()
973
974        ctx1 = context.WovenContext()
975        ctx2 = context.WovenContext()
976
977        ctx1.remember(p1, inevow.IRendererFactory)
978        ctx2.remember(p2, inevow.IRendererFactory)
979
980        p1_str = p1.renderSynchronously(ctx1)
981        p2_str = p2.renderSynchronously(ctx2)
982
983        self.assertNotEquals(p1_str, p2_str)
984
985    def test_macroInsideSpecialScope(self):
986        """http://divmod.org/trac/ticket/490
987        """
988        class Base(rend.Page):
989            def macro_content(self, ctx):
990                return p["content"]
991
992        class Page1(Base):
993            docFactory = loaders.stan(
994                html[
995                    body(render=directive('foo'))[
996                        p(macro=directive('content'))
997                    ]
998                ])
999
1000            def render_foo(self, ctx, data):
1001                return ctx.tag
1002
1003        class Page2(Base):
1004            docFactory = loaders.stan(
1005                html[
1006                    body[
1007                        p(macro=directive('content'))
1008                    ]
1009                ])
1010
1011        p1 = Page1()
1012        p2 = Page2()
1013
1014        ctx1 = context.WovenContext()
1015        ctx2 = context.WovenContext()
1016
1017        ctx1.remember(p1, inevow.IRendererFactory)
1018        ctx2.remember(p2, inevow.IRendererFactory)
1019
1020        p1_str = p1.renderSynchronously(ctx1)
1021        p2_str = p2.renderSynchronously(ctx2)
1022
1023        self.assertEquals(p1_str, p2_str)
1024
1025